生成非连续组合

2024-05-05

我正在尝试创建一个生成器(支持执行 next 的迭代器,可能在 python 中使用yield),它给出来自 {1,2,...n} (n 和 r 是参数)的 r 元素的所有组合,这样在选出的r个元素,没有两个是连续的。

例如,对于 r = 2 且 n= 4

生成的组合是{1,3}, {1,4}, {2, 4}.

我可以生成所有组合(作为迭代器)并过滤那些不满足条件的组合,但我们将做不必要的工作。

是否有一些生成算法使得next是 O(1)(如果不可能,则为 O(r) 或 O(n))。

返回集合的顺序并不相关(并且希望允许 O(1) 算法)。

注意:我已将其标记为 python,但与语言无关的算法也会有所帮助。

Update:

我找到了一种将其映射到生成纯组合的方法!网络搜索显示 O(1) 的组合是可能的(尽管看起来很复杂)。

这是映射。

假设我们有一个组合x_1, x_2, ... , x_r with x_1 + 1 < x_2, x_2 + 1 < x_3, ...

我们映射到y_1, y_2, ..., y_r如下

y_1 = x_1

y_2 = x_2 - 1

y_3 = x_3 - 2

...

y_r = x_r - (r-1)

这样我们就有了y_1 < y_2 < y_3 ...没有非连续约束!

这基本上相当于从 n-r+1 中选择 r 个元素。因此,我需要做的就是运行 (n-r+1 选择 r) 的生成。

就我们的目的而言,在生成事物后使用映射就足够了。

选择svkcr的答案的理由

所有很好的答案,但我选择了 svkcr 的答案。

以下是一些原因

  1. 它实际上是无状态的(或更准确地说是“马尔可夫”)。下一个排列可以从前一个排列生成。它在某种程度上几乎是最优的:空间和时间为 O(r)。

  2. 这是可以预见的。我们确切地知道组合生成的顺序(字典顺序)。

这两个属性使得并行生成变得容易(在可预测的点和委托处分割),并引入容错功能(如果 CPU/机器出现故障,可以从最后生成的组合中挑选)!

抱歉,前面没有提到并行化,因为我在写问题时并没有想到它,只是后来才得到这个想法。


这个很有趣!这个怎么样:

def nonconsecutive_combinations(r, n):
  # first combination, startng at 1, step-size 2
  combination = range(1, r*2, 2)
  # as long as all items are less than or equal to n
  while combination[r-1] <= n:
    yield tuple(combination)
    p = r-1 # pointer to the element we will increment
    a = 1   # number that will be added to the last element
    # find the rightmost element we can increment without
    # making the last element bigger than n
    while p > 0 and combination[p] + a > n:
      p -= 1
      a += 2
    # increment the item and
    # fill the tail of the list with increments of two
    combination[p:] = range(combination[p]+1, combination[p] + 2*(r-p), 2)

Each next()调用应该有一个 O(r) .. 我在思考如何将其转换为自然数时得到了这个想法,但花了相当长的时间才得到正确的细节。

> list(nonconsecutive_combinations(2, 4))
[(1, 3), (1, 4), (2, 4)]
> list(nonconsecutive_combinations(3, 6))
[(1, 3, 5), (1, 3, 6), (1, 4, 6), (2, 4, 6)]

让我尝试解释一下这是如何工作的。

元组的条件c with r作为结果集一部分的元素:

  1. 元组中的任何元素至少与前一个元素加 2 一样大。 (c[x] >= c[x-1] + 2)
  2. 所有元素都小于或等于n。 因为 1. 说这一点就足够了最后一个元素小于 或等于n. (c[r-1] <= n)

最小的元组may成为结果集的一部分是(1, 3, 5, ..., 2*r-1)。 当我说一个元组比另一个元组“小”时,我假设的是字典顺序。

正如 Blckknght 所指出的,即使是最小的可能元组也可能太大而无法满足条件 2。

上面的函数包含两个 while 循环:

  • 外循环遍历结果并假设它们按字典顺序出现并满足条件一。一旦有问题的元组违反了条件二,我们就知道我们已经用尽了结果集并且完成了:

    combination = range(1, r*2, 2)
    while combination[r-1] <= n:
    

    第一行根据条件一用第一个可能的结果初始化结果元组。第二行直接转化为条件二。

  • 内循环查找满足条件一的下一个可能的元组。

    yield tuple(combination)
    

    自从while条件(2)为真,并且我们确保结果满足条件一,我们可以产生当前的结果元组。

    接下来,为了找到按字典顺序排列的下一个元组,我们将向最后一个元素添加“1”。

    # we don't actually do this:
    combination[r-1] += 1
    

    然而,这可能会过早地破坏条件 2。所以,if该操作会破坏条件 2,我们增加前面的元素并相应地调整最后一个元素。这有点像计算以 10 为基数的整数:“如果最后一位数字大于 9,则递增前一位数字并使最后一位数字为 0”。但我们不是添加零,而是填充元组以使条件 1 为真。

    # if above does not work
    combination[r-2] += 1
    combination[r-1]  = combination[r-2] + 2
    

    问题是,第二行可能会再次违反条件二。所以我们实际做的是,我们跟踪最后一个元素,这就是对a。我们还使用p变量来引用我们正在查看的当前元素的索引。

    p = r-1
    a = 1
    while p > 0 and combination[p] + a > n:
      p -= 1
      a += 2
    

    我们从右到左(p = r-1,p -= 1)迭代结果元组的项目。 最初,我们想要向最后一项添加 1 (a = 1),但是当单步执行元组时,我们实际上想要将最后一项替换为前一项的值加上2*x, where x是两个项目之间的距离。 (a += 2, 组合[p] + a)

    最后,我们找到了要增加的项,并用从增加的项开始的序列填充元组的其余部分,步长为 2:

    combination[p:] = range(combination[p]+1, combination[p] + 2*(r-p), 2)
    

    就是这样。当我第一次想到它时,它看起来很简单,但是整个函数中的所有算术都为差一错误提供了一个很好的地方,并且描述它比应该的更难。当我添加内部循环时我应该知道我遇到了麻烦:)

论性能..

不幸的是,充满算术的 while 循环并不是用 Python 编写的最有效的东西。其他解决方案接受这一现实,并使用列表理解或过滤将繁重的工作推入 Python 运行时。这在我看来是正确的做法.

另一方面,我非常确定,如果这是 C,我的解决方案会比大多数解决方案执行得更好。内部 while 循环是 O(log r) ,它会就地改变结果,并且相同的 O(log r )。它不消耗额外的堆栈帧,并且除了结果和两个变量之外不消耗任何内存。但显然这不是 C,所以这些都不重要。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

生成非连续组合 的相关文章

随机推荐

  • 如何简化 Swift Enum 自定义 init

    我创建了一个带有字符串类型的枚举 它有两个 init 方法 一种是使用 rawValue 的默认 init 方法 另一种是使用 intValue 的自定义 init 方法 我是这样写的 有没有什么简单的方法可以不使用两个开关盒 enum R
  • 如何在 VC++ 项目中引用 DLL

    我有一个正在尝试编译的 C 驱动程序 它的代码中有这一行 import msado15 dll no namespace rename EOF EndOfFile 但是当我编译项目时 出现错误 错误1致命错误C1083 无法打开类型库文件
  • 如何设置 TeamCity NuGet 安装程序的 MSBuild 版本?

    我正在尝试使用以下命令恢复 NET Core 解决方案的 NuGet 包NuGet 安装程序 https confluence jetbrains com display TCD10 NuGet InstallerTeamCity 构建步骤
  • 多重继承中使用delete操作符时谁调用类的析构函数

    这个问题听起来可能太愚蠢了 但是 我在其他地方找不到具体的答案 对后期绑定如何工作以及继承中使用的 virtual 关键字知之甚少 如代码示例中所示 在继承的情况下 当使用指向在堆上创建的派生类对象的基类指针和删除运算符来释放内存时 将仅按
  • Powershell脚本运行带有参数的exe文件

    我需要脚本来运行带参数的 exe 文件 我就是这么写的 请问有更好的方法吗 Command Networkpath Restart exe Parms t 21600 m 360 r f Prms Parms Split Command P
  • 如何编写三元运算符(又名 if)表达式而不重复自己

    例如 这样的事情 var value someArray indexOf 3 1 someArray indexOf 3 0 有更好的写法吗 再说一遍 我并不是在寻求上述问题的答案 只是一个在三元运算符表达式中可能重复操作数的示例 就我个人
  • 使用 grep 查找两个字符之间的字符串

    我发现了这一点answer https stackoverflow com a 1454936 2068595用于查找两个字符之间的字符串的正则表达式 就我而言 我想找到之间的每一个模式 and 这是正则表达式 lt 确实 当我尝试它时它有
  • DrawerLayout 第一次打开有点步骤

    我有一个 DrawerLayout 当我第一次滑动它时 它是逐步出现的 比如滞后或类似的东西 但之后它移动得很好 我不知道要发布什么代码 因为正如我所说 它工作正常 只是第一次打开它时 它打开不顺利 这是我的布局
  • 在隐藏字段中创建 has_many 关联

    说用户 has many Things 在用户表单中 我想要一个隐藏字段 它可以让我在这个新用户和预先存在的事物 例如 id 8 之间创建关系 以下代码片段有什么问题 我想我只是忘记了一些语法 对于后代 如果您有多个 事物 值需要以数组形式
  • 选择特定值之后的项目

    说这是我的sql SELECT title author ISBN FROM bs books ORDER BY ISBN LIMIT 3 它只是从某个表中选择所有内容 标题 作者等 假设我想选择某个标题后面的所有项目 而不是按字母顺序或其
  • Servlet 包含 Tomcat 中的 HTTP 标头

    我有一个 servlet 它的请求调度程序包含另一个 servlet 包含的 servlet 设置了我想在包括小服务程序 因此 我在 include 方法中传入一个自定义 HTTPResponse 对象 该对象捕获来自 servlet 的所
  • 在 Xcode 中添加较大的 Power 值

    我是 Objective C 和 iPhone 开发的新手 我正在使用基本计算器 我想在文本字段中添加一个大值 如何在 Xcode 中显示像这样的大值 6 67543 x 10 34 谢谢 您可以使用 NSNumberFormatterSc
  • C 中的数组初始化

    我对以下代码有疑问 int main int array1 1 2 3 4 5 error in c warning in c int array2 1 2 3 4 5 int array3 5 1 2 3 4 5 这段代码在第 3 行给出
  • 在 Python 3.5 64 位上通过 pip 安装 OpenCV

    我尝试安装 OpenCV 但找不到任何合适的 pip 软件包 我决定上网查找有关如何安装它的官方文档 并发现this https opencv python tutroals readthedocs io en latest py tuto
  • DbContext 因 PrimitiveType != null 错误而崩溃

    使用 Entity Framework Code First Web 应用程序在调用 DbContext 时崩溃 并出现以下错误 断言失败 表达式 primitiveType null 描述 断言失败 primitiveType null
  • 如何正确定向从 AVCaptureVideoDataOutputSampleBufferDelegate 生成的图像

    我在用着AVCaptureVideoDataOutputSampleBufferDelegate我收到一个CMSampleBufferRef我将其转换为UIImage 但生成的图像方向不正确 Get a CMSampleBuffer s C
  • bitblt 在 Windows 10 版本 1703 上失败 (15063.138)

    使用 Visual Studio 2017 vc141 以下代码应该从前游戏窗口获取屏幕截图 但现在它返回黑色和空白图像 唯一的游戏问题 尝试过 OpenGL 和 Vulkan ogl 返回黑色 vulkan 返回白色 在升级到 Windo
  • 是否可以在 Flutter 中创建自定义快速设置图块?

    我搜索了 Flutter 文档并用谷歌搜索了这个 但结果为零 我正在开发我的第一个 Android Flutter 应用程序 我想为其创建一个自定义的快速设置图块 我的目标是牛轧糖及以上 我知道这在 Java 和 Kotlin 中是可能的
  • ios safari - getUserMedia 无法正常工作

    我真的有this https stackoverflow com q 45692526 6048715问题 但 OP 的解决方案对我不起作用 重申一下 我正在使用navigator mediaDevices getUserMedia 在浏览
  • 生成非连续组合

    我正在尝试创建一个生成器 支持执行 next 的迭代器 可能在 python 中使用yield 它给出来自 1 2 n n 和 r 是参数 的 r 元素的所有组合 这样在选出的r个元素 没有两个是连续的 例如 对于 r 2 且 n 4 生成