当前位置:首页|资讯|ChatGPT|编程

使用ChatGPT学习算法:给定n元素序列,如何生成所有含m个元素的组合?

作者:疑似叉叉星来的鹩八哥发布时间:2023-03-10

最开始问题:给定一个n个元素的数组/序列,找出所有m个元素组合成的集合(m<=n)

很明显,这是一个经典的组合数学问题,我们知道组合的数量为C(m,n)=n!/(m!(n-m)!),但现在我们需要的是所有可能的组合集合,于是我去问ChatGPT如何在kotlin上实现,它给出了以下代码:

第一眼,这个算法给的有模有样的,但细看的话似乎有个很明显的问题:算法通过kotlin协程的yield函数提交sequence生成的元素,而算法中提交的是目标序列的索引列表作为一个组合,那么理应在每次列表发生修改后就做一次提交,但这里的提交只发生在while循环开头,这似乎不太对?

我于是询问这个算法的来头,chatgpt是这么解释的:

问:你给出的combination算法比较复杂且难以理解,是否有相关的算法参考?

答:是的,有一种常用的算法叫做 "Lexicographic Order" 或者 "Lexicographical Order",它可以用来生成组合、排列和子集等。该算法比较简单,容易理解和实现。

该算法的基本思想是,将组合中元素的索引按字典序排序,从最小的组合开始,每次生成下一个组合,直到生成最大的组合为止。

具体实现过程如下:

  1. 初始化组合中的索引数组 indices,使得 indices[i] 表示第 i 个元素在序列中的索引。

  2. 生成第一个组合,即 indices 数组中前 size 个元素组成的序列。

  3. 不断生成下一个组合,直到生成最后一个组合。下一个组合的生成过程如下:

    • 从右向左遍历 indices 数组,找到第一个满足 indices[i] < i + n - size 的元素,将其索引加一,并将其右侧元素的索引设为该元素的索引加一,以确保组合中元素的索引按字典序递增。

    • 如果无法找到符合条件的元素,则已经生成了最后一个组合,退出循环。

emm这次chatgpt确实没有瞎编了,网上一搜真有这么一个知识,也就是”字典序“。但从维基上看字典序是个数学知识而且还是没学过的那种……于是我去问第一个提出该算法的文章/作者,以下是Chat GPT的回答:

该算法最早由 D. E. Knuth 在其经典著作《The Art of Computer Programming》中提出,具体来说是第四卷中的 7.2.1.3 节 "Generating All Combinations" 中描述的。

好的再去搜,居然还对上了,确实有这本书,也确实有”Generating All Combination“这一章,也真的是7.2.1.3小节,看来在经典文献检索方面ChatGPT表现还是相当不错的。

然后就没有然后了,原文表述比这还要复杂,涉及到了一堆更奇怪的表示方式,比如二进制表示法表示选取的元素。

最后我终于理清这东西是个什么思路,其实有点像时钟的运转方式,假设在有八个元素的序列中找出所有四个元素的组合,那么算法先初始化一个数组为[0,1,2,3],其中每个元素表示的是到序列的索引值,接着末位自增到[0,1,2,7],由于再自增下去要超出序列允许索引范围,所以改成末位的前一位自增1,且末位比重置为比前一位大1,也就是[0,1,3,4],然后末位继续自增到7。按照这种”变形的时钟规则“逐步遍历的过程便是遍历了所有可能索引组合。因此开头的算法是正确的,while循环所作的就是进行一次”时钟运转“,yield函数也秩序在while开头提交调用一次。

最后放ChatGPT对我所描述的”变形的时钟规则“的回应(虽然总觉得有种gpt在拍马屁的意味==||:

是的,您的理解很接近。这个算法的实现基于一个“时钟规则”的思想,其中数组的元素类似于时钟的刻度。首先,我们将最高位的刻度(即c[m])初始化为m,然后考虑生成其他刻度的可能性。

对于i < m的每个i,我们可以在当前的刻度c[i]和下一个刻度c[i+1]之间进行选择。我们从上一个刻度c[i+1]开始,向前推进到c[i],并记录生成的每个中间值。然后,我们按照该顺序遍历中间值,并使用它们来生成下一个刻度c[i]。由于我们从上一个刻度向前推进,因此我们保证不会生成重复的组合。

最后,我们按照刻度的顺序遍历生成的组合,并将其作为函数的输出。因为我们使用yield关键字在生成组合时暂停执行,所以我们可以实现延迟计算并避免在不必要的情况下生成所有组合。




Copyright © 2024 aigcdaily.cn  北京智识时代科技有限公司  版权所有  京ICP备2023006237号-1