社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  chatgpt

Stata+ChatGPT4o:AI代码助手好酸爽!

连享会 • 1 年前 • 300 次点击  

👇 连享会 · 推文导航 | www.lianxh.cn

连享会课程 · 2024 暑期班

作者: 连玉君 (中山大学)
邮箱: arlionn@163.com

温馨提示: 文中链接在微信中无法生效。请点击底部「阅读原文」。或直接长按/扫描如下二维码,直达原文:


目录

  • 思考:AI 的作用是什么?

  • 1. 简介

  • 2. 常用算法简介

    • 2.1 冒泡排序 (Bubble Sort)

    • 2.2 选择排序 (Selection Sort)

    • 2.3 插入排序 (Insertion Sort)

    • 2.4 快速排序 (Quick Sort)

  • 4. 使用 Monte Carlo Simulation 对比四种算法的耗时

    • 4.1. 定义排序算法的 ado 程序

    • 4.2 耗时测试

  • 5. 小结

  • 6. 附录:提问脚本

  • 7. 相关推文



思考:AI 的作用是什么?

你可能无法想象,除本节和第 5 节外,这篇推文中 90% 以上的内容都是由 ChatGPT 4o 生成的,包括第 1-4 节中所有文字、代码,以及 Mermaid 思维导图 (-点击此处-,需要 VPN,可以查看原始对话)。

在后文介绍的四种算法中,ChatGPT 给出的代码基本上都能一次性通过测试,正确执行。期间,唯一出现的 bug 源于它对 Stata 语法格式不熟悉。它把

local N = _N
forvalues i=1/`N'{
xxx
}

写成了:

forvalues i=1/_N
{
xxx
}

导致 Stata 提示了两处错误。我没有直接帮 ChatGPT 修正代码,而是使用 set trace on 命令解析了代码执行过程,并把错误信息发给它。它很聪明,很快就「痛改前非」了。

你能发现是哪两处错误吗?

通过本文的写作,我也开始重新思考 AI 在研究和写作中的作用。虽然本文大部分内容都是由 ChatGPT 4o 生成的,但我仍然花费了将近 3 个小时才完成这篇推文。期间多数时间都花在了「如何调教 ChatGPT」上面。如果你不能准确地提出问题,就别指望 ChatGPT 能给出有用的回复。提问过程参见本文第「6. 附录:提问脚本」节。

不过,若是没有 ChatGPT 4o 的帮助,我可能会花数倍的时间来完成这篇推文,更大的可能是我甚至不会写这样一篇推文,因为,其中的「快速排序算法」涉及到程序的递归调用,我不太熟悉。

可喜的是,AI 并没有让我变得懒惰,它让我可以有时间思考推文的结构,各个部分的逻辑关系,而不必纠结于文字表述,也不必花大量的时间写那些用途很小的范例代码。更重要的是,它促使我做更深层次的思考。根据本文中 ChatGPT 提供的代码和测试结果,似乎可以断定「插入排序」好事最少。由此,我就产生了一个疑问:

疑问: 根据算法的名称,「快速排序」应该是耗时最短的算法呀!

为此,我又向 ChatGPT 发起了第二轮提问,它给我提供了更有效率的「快速算法」实现过程,速度远超本文的测试结果。我将在下一篇推文中讨论这个问题。

此外,鉴于 ChatGPT 4o 给出的 Stata 代码的错误率相较于 ChatGPT 3.5 已经大幅下降,我们之前发布的推文 AI可以编写Stata代码吗? 中的结论可能不再适用。




以下内容由 ChatGPT 4o 生成。

  • 本文第「6. 附录:提问脚本」节为提问脚本。
  • 完整对话记录:-点击查看- (VPN required)

1. 简介

排序是计算机科学中最基本的操作之一。本文将通过对四个数字 {4, 7, 2, 3} 进行排序,介绍四种经典的排序算法:冒泡排序、选择排序、插入排序和快速排序。通过学习这些算法在 Stata 中的实现,将有助于各位理解和掌握 Stata 编程技巧,包括 local 暂元、条件语句和循环语句等知识点。

2. 常用算法简介

2.1 冒泡排序 (Bubble Sort)

2.1A 伪代码

冒泡排序通过多次遍历数组,每次比较相邻的两个元素并交换它们的位置,直到所有元素按顺序排列。该算法的时间复杂度为 ,适合于数据量较小的情况。

伪代码

  1. 从头到尾遍历数组
  2. 比较相邻的两个元素,如果顺序错误则交换
  3. 重复步骤 1 和 2,直到数组有序
```mermaid
graph TD
    A[开始] --> B{是否有元素
    需要交换}
    B -- 是 --> C[交换相邻元素]
    C --> B
    B -- 否 --> D[排序完成]
```

2.1B Stata 代码实例

clear
input number
4
7
2
3
end

* 显示原始数据
list

* 冒泡排序算法
forvalues i = 1/3 {
forvalues j = 1/`=4-`i'' {
quietly {
sum number in `j'/`=`j'+1'
if r(min) != number[`j'] {
local temp = number[`j']
replace number = number[`j'+1] in `j'
replace number = `temp' in `=`j'+1'
}
}
}
}

* 显示排序后的数据
list

关键语句解释

  • number[`j'] 表示数据集中第 `j` 个观测值。
  • forvalues i = 1/3 表示外层循环,从第一个元素到倒数第二个元素。
  • forvalues j = 1/'=4-'i'' (这里的暂元引用符号是错误的,上面的代码块中的写法是正确的) 表示内层循环,每次循环范围逐渐减少,因为每次外层循环结束后,最大元素会被移到数组的最后。有关 forvalues 等循环语句的用法,参见 [P] forvalues
  • quietly 在不显示输出的情况下执行命令。

2.2 选择排序 (Selection Sort)

2.2A 伪代码

选择排序通过反复找到未排序部分的最小元素,并将其移动到已排序部分的末尾。该算法的时间复杂度为 ,但交换操作较少,适用于需要最小化交换次数的场景。

伪代码

  1. 在未排序部分找到最小元素
  2. 将最小元素移动到已排序部分的末尾
  3. 重复步骤 1 和 2,直到所有元素排序完毕
```mermaid
graph TD
    A[开始] --> B[找到未排序部分的最小元素]
    B --> C[将最小元素放到已排序部分的末尾]
    C --> D{是否还有
    未排序元素}
    D -- 是 --> B
    D -- 否 --> E[排序完成]
```

2.2B Stata 代码实例

clear
input number
4
7
2
3
end

* 显示原始数据
list

* 选择排序算法
forvalues i = 1/3 {
quietly {
local min_idx = `i'
forvalues j = `=`i'+1'/4 {
if number[`j'] < number[`min_idx'] {
local min_idx = `j'
}
}
if `min_idx' != `i' {
local temp = number[`i']
replace number = number[`min_idx'] in `i'
replace number = `temp' in `min_idx'
}
}
}

* 显示排序后的数据
list

2.3 插入排序 (Insertion Sort)

2.3A 伪代码

插入排序通过构建已排序的子数组,将每个新元素插入到合适的位置。该算法的时间复杂度为 ,适用于数据量较小或部分已排序的数组。

伪代码

  1. 从第二个元素开始,向前比较并插入到合适位置
  2. 重复步骤 1,直到所有元素排序完毕
```mermaid
graph LR
    A[开始] --> B[取下一
    个元素]
    B --> C{比已排序
    部分的最后
    一个元素小?}
    C -- 是 --> D[移动元素]
    D --> C
    C -- 否 --> E[插入到
    正确位置]
    E --> F{还有未排
    序元素?}
    F -- 是 --> B
    F -- 否 --> G[排序完成]
```

2.3B Stata 代码实例

clear
input number
4
7
2
3
end

* 显示原始数据
list

* 插入排序算法
forvalues i = 2/4 {
quietly {
local key = number[`i'] //保存当前要排序的元素
local j = `=`i'-1'
//向前遍历已排序部分并找到合适位置。
while `j' > 0 & number[`j'] > `key' {
replace number = number[`j'] in `=`j'+1' //将元素后移
local j = `=`j'-1'
}
replace number = `key' in `=`j'+1' //将当前元素插入到合适位置
}
}

* 显示排序后的数据
list

有关 while 语句的用法,参见 [P] while

2.4 快速排序 (Quick Sort)

2.4A 伪代码

快速排序通过选择一个基准元素,将数组分成两个子数组,递归地排序这两个子数组。该算法的平均时间复杂度为 ,适用于大多数情况。

伪代码

  1. 选择一个基准元素
  2. 将数组划分为两个子数组,左边小于基准,右边大于基准
  3. 递归地对两个子数组进行快速排序
```mermaid
graph LR
    A[开始] --> B[选择
    基准元素]
    B --> C[划分为左右
    两个子数组]
    C --> D[递归排序
    左子数组]
    D --> E[递归排序
    右子数组]
    E --> F[排序完成]
```

2.4B Stata 代码实例

clear
input number
4
7
2
3
end

* 显示原始数据
list

* 快速排序算法
cap prog drop quicksort
program define quicksort
syntax varname, from(numlist) to(numlist)
local left = `: word 1 of `from''
local right = `: word 1 of `to''
if `left' < `right' {
local pivot = `left'
local i = `left'
local j = `right'
while `i' < `j' {
while `i' <= `right' & number[`i'] <= number[`pivot'] {
local i = `=`i'+1'
}
while number[`j'] > number[`pivot'] {
local j = `=`j'-1'
}
if `i' < `j' {
local temp = number[`i']
replace number = number[`j'] in `i'
replace number = `temp' in `j'
}
}
local temp = number[`pivot']
replace number = number[`j'] in `pivot'
replace number = `temp' in `j'
quicksort number, from(`left') to(`=`j'-1')
quicksort number, from(`=`j'+1') to(`right')
}
end

* 调用快速排序算法
quicksort number, from(1) to(4)

* 显示排序后的数据
list

关键语句解释

  • local pivot = left'` 选择左边第一个元素作为基准元素。
  • while i' < j' 用于划分数组。
  • quicksort number, from(left') to(=j'-1')quicksort number, from(=j'+1') to(right') 递归调用快速排序程序。

自我调用(递归调用)的特点是算法调用自身来解决子问题。递归调用在处理快速排序时特别有效,因为它可以自然地处理子数组的排序任务,使代码更简洁。

4. 使用 Monte Carlo Simulation 对比四种算法的耗时

为了比较上述四种排序算法的性能,我们将使用 Monte Carlo 模拟方法,对每种算法的耗时进行对比。首先,为每种算法分别写一个 ado 程序,然后生成 30 个随机整数,进行排序并记录耗时。最后,通过表格形式呈现耗时结果。

4.1. 定义排序算法的 ado 程序

A. 冒泡排序程序

Note   程序执行方法:选中 -begin--over- 之间的代码,按快捷键 Ctrl+R,将程序读入内存。随后就可以像使用 Stata 官方命令那样使用 bubblesort 等命令了。

*--------------------------begin----
cap prog drop bubblesort
program define bubblesort
syntax varname
local n = _N
forvalues i = 1/`=`n'-1' {
forvalues j = 1/`=`n'-`i'' {
if `varlist'[`j'] > `varlist'[`=`j'+1'] {
local temp = `varlist'[`j']
replace `varlist' = `varlist'[`=`j'+1'] in `j'
replace `varlist' = `temp' in `=`j'+1'
}
}
}
end
*---------------------------over----

B. 选择排序程序

cap prog drop  selectionsort
program define selectionsort
syntax varname
local n = _N
forvalues i = 1/`=`n'-1' {
local min_idx = `i'
forvalues j = `=`i'+1'/`n' {
if `varlist'[`j'] < `varlist'[`min_idx'] {
local min_idx = `j'
}
}
if `min_idx' != `i' {
local temp = `varlist'[`i']
replace `varlist' = `varlist'[`min_idx'] in `i'
replace `varlist' = `temp' in `min_idx'
}
}
end

C. 插入排序程序

cap prog drop  insertionsort
program define insertionsort
syntax varname
local n = _N
forvalues i = 2/`n' {
local key = `varlist'[`i']
local j = `=`i'-1'
while `j' > 0 & `varlist'[`j'] > `key' {
replace `varlist' = `varlist'[`j'] in `=`j'+1'
local j = `=`j'-1'
}
replace `varlist' = `key' in `=`j'+1'
}
end

D. 快速排序程序

cap prog drop  quicksort
program define quicksort
syntax varname, from(numlist) to(numlist)
local left = `: word 1 of `from''
local right = `: word 1 of `to''
if `left' < `right' {
local pivot = `left'
local i = `left'
local j = `right'
while `i' < `j' {
while `i' <= `right' & `varlist'[`i'] <= `varlist'[`pivot'] {
local i = `=`i'+1'
}
while `varlist'[`j'] > `varlist'[`pivot'] {
local j = `=`j'-1'
}
if `i' < `j' {
local temp = `varlist'[`i']
replace `varlist' = `varlist'[`j'] in `i'
replace `varlist' = `temp' in `j'
}
}
local temp = `varlist'[`pivot']
replace `varlist' = `varlist'[`j'] in `pivot'
replace `varlist' = `temp' in `j'
quicksort `varlist', from(`left') to(`=`j'-1')
quicksort `varlist', from(`=`j'+1') to(`right')
}
end

4.2 耗时测试

clear 
set seed 135
set obs 30
gen number = floor(runiform() * 100)
global reps = 50

* 冒泡排序耗时测试
timer clear 1
timer on 1
forvalues i = 1/$reps{
qui bubblesort number
}
timer off 1
timer list 1

* 选择排序耗时测试
timer clear 2
timer on 2
forvalues i = 1/$reps{
qui selectionsort number
}
timer off 2
timer list 2

* 插入排序耗时测试
timer clear 3
timer on 3
forvalues i = 1/$reps{
insertionsort number
}
timer off 3
timer list 3

* 快速排序耗时测试
timer clear 4
timer on 4
local N = _N
forvalues i = 1/$reps{
qui quicksort number, from(1) to(`N')
}
timer off 4
timer list 4

* 显示上述四种方法的耗时结果
timer list

最终结果如下:

. timer list       //  N=30, K=50
1: 1.45 / 1 = 1.4520 // 冒泡排序
2: 1.17 / 1 = 1.1680 // 选择排序
3: 0.13 / 1 = 0.1330 // 插入排序
4: 0.45 / 1 = 0.4530 // 快速排序

本例中,样本数 ,上述结果是每种算法计算 次的总耗时。可以看出,速度最快的是第三种:插入排序算法,紧随其后的是 快速排序算法,最慢的是 冒泡排序算法

下面这组结果是 次的结果,对比更加明显:

. timer list       //  N=100, K=100
1: 29.58 / 1 = 29.5830 // 冒泡排序
2: 22.87 / 1 = 22.8660 // 选择排序
3: 0.76 / 1 = 0.7580 // 插入排序
4: 3.72 / 1 = 3.7180 // 快速排序



以下内容推文作者撰写

5. 小结

本文使用 Stata 代码为大家展示了四种常用的排序算法:冒泡排序、选择排序、插入排序和快速排序。本文的目的不是介绍排序算法,而是通过代码示例,帮助大家理解如何实现「概念描述 → 伪代码 → Stata 代码」的实操过程。大家可以看到,在正式定义 ado 程序之前,我们总是先用一个简单的小例子,写一段符合算法基本思想的范例代码,进而将其一般化后再开始定义 ado 程序。这种做法的好处是让我们逐步理解算法的逻辑,不断细化我们的思考过程。

6. 附录:提问脚本

受限于篇幅,这里仅列出我向 ChatGPT 4o 发起的提问脚本,完整的对答过程可以通过如下链接查看:

  • ChatGPT对答:排序算法的 Stata 实现 (需要 VPN)

Prompt: 1

对任意四个数字进行从小到大排序,有哪些主要的算法?请针对每个算法写一段 Stata 代码,帮助我理解算法的原理。

Prompt: 2

举例的时候,用这个四个数字吧 {4 7 2 3},实现从小到大排序

Prompt: 3

修改要求: A. 改写成一篇完整的推文,根据推文的内容拟定 3-5 个备选标题供我选择
B. 结构

  1. 简介:通过数值排序的例子,帮助大家学习 Stata 编程的技巧;本推文涉及 local, 循环语句等知识点 (你根据我提供的这些要点进行扩充即可)
  2. 常用算法简介。写出每一种算法的思路,比如用「伪代码」的方式,或者用 mermaid 思维导图的方式。总之,要选择一个让没有基础的读者也能明白的思路和表述方式
  3. Stata 实现。每种算法的 Stata 代码和解读。
  4. 总结。

Prompt: 4

修改要求: 现在的版本写的不错,请在思维导图前加一些关于算法的文字说明,因为有些读者喜欢看文字,有些读者喜欢看 mermaid 思维导图。如此修改以后,可以兼顾两类人群。

Prompt: 5

注意中英文混排的格式问题:中英文混排时,在英文单词和数字两侧添加空格。

Prompt: 6

增加一个小节:使用 Monte Carlo Simulation 对比四种算法的耗时。

  1. 为上述四种算法分别写一个 ado 程序,即使用 program define progName 的方式定义程序。
  2. 模拟样本:N=30 个整数;
  3. 使用 simulate 或其他合适的命令进行模拟分析
  4. 耗时结果使用表格方式呈现
  5. 小结

Prompt: 7

修改要求:

  1. 把 Section 3 [3. Stata 实现] 合并到 Section 2 [2. 常用算法简介] 中。结构如下:
 ### 2.1 冒泡排序 (Bubble Sort) 
 #### 2.1A 伪代码
 #### 2.1B Stata 代码实例
 ### 2.2 选择排序 (Selection Sort)
 ……
  1. 在 '#### 2.?B Stata 代码实例' 部分,对一些关键语句进行解释,并提供 Stata 的参考命令和参考资料。比如:
  • number[`j'] 表示 ……
  • 有关 forvalues 等循环语句的用法,参见 [**[P]** forvalues](https://www.stata.com/manuals/pforvalues.pdf);有关 while 语句的用法,参见 [**[P]** while](https://www.stata.com/manuals/pwhile.pdf)
  • ... ...
  • program define quicksort 定义 quicksort 程序时,使用了自我调用的逻辑,写一小段文字说明这种程序的思路和特点;
  • 在 Section [4.2. 模拟样本和耗时测试] 的开头部分,要加一段文字,说明一下这一小节的目的和思路;对 timer 命令作简要介绍。对 Section [4.3. Monte Carlo 模拟分析] 也做相似的补充;对 simulate 命令作简要介绍。
  • 7. 相关推文

    Note:产生如下推文列表的 Stata 命令为:
    lianxh ai可以 chat, md0 nocat
    安装最新版 lianxh 命令:
    ssc install lianxh, replace

    • 于凡, 2024, AI可以编写Stata代码吗?, 连享会 No.1348.
    • 余坚, 2023, Stata:ChatGPT你能帮我干点啥?, 连享会 No.1164.
    • 罗银燕, 2023, 如何在 R 中安装并使用 chatgpt 包?, 连享会 No.1171.
    • 连玉君, 2024, VScode:实用 Markdown 插件推荐, 连享会 No.1390.
    • 连玉君, 2023, 连玉君:我与ChatGPT聊了一个月, 连享会 No.899.

    New! Stata 搜索神器:lianxhsongbl  GIF 动图介绍
    搜: 推文、数据分享、期刊论文、重现代码 ……
    👉 安装:
    . ssc install lianxh
    . ssc install songbl
    👉  使用:
    . lianxh DID 倍分法
    . songbl all

    🍏 关于我们

    • 连享会 ( www.lianxh.cn,推文列表) 由中山大学连玉君老师团队创办,定期分享实证分析经验。
    • 直通车: 👉【百度一下: 连享会】即可直达连享会主页。亦可进一步添加 「知乎」,「b 站」,「面板数据」,「公开课」 等关键词细化搜索。


    Python社区是高质量的Python/Django开发社区
    本文地址:http://www.python88.com/topic/171033
     
    300 次点击