在一大堆数字中找到最常见的六胞胎

Finding most common sextuplets in a huge set of numbers

本文关键字:常见 一大堆 数字      更新时间:2023-10-16

我有一个矩阵6000x20,包含的数字范围从1-80。我需要找到该矩阵中出现的所有六胞胎。我需要最有效、最快的解决方案。我目前的解决方案是以下步骤:1.我从矩阵中取第一行并生成所有六胞胎(一行38600(2.我将六行中的每一行与其他5999行进行比较并计算它们3.我将它们写入文件,因为我的内存很快就会填满4.我采取第二行并生成所有六胞胎,然后再次执行所有步骤

这个算法非常糟糕,我知道它,因为我有 38600X6000 的比较和可能的文件写入,而且我有很多六胞胎重复。但我无法知道,因为我不能使用这种大小的变量。

需要一个算法解决方案,这样我就可以用matlab/java/c++/python编写它

由于值的范围从1到80,可能的六胞胎总数"仅"超过3亿(准确地说是300,500,200(。由于矩阵中只有 6000 行,因此任何六胞胎的最大计数为 6000,因此计数将舒适地适合一个双字节整数(uint16_t,假设存在于您的C++实现中(。三亿个双字节整数总共 600 兆字节,您可能已经可用。

因此,一个简单的算法是创建计数向量,初始化为零,然后迭代矩阵中的所有行;对于每一行,迭代38,760个六胞胎,对于每个六胞胎,递增相应的计数。

诀窍是弄清楚计数向量中的哪个元素对应于给定的六个数字集。碰巧的是,只要六胞胎中的数字从小到大依次排列,这并不难。(这不是限制,因为您需要为六胞胎设置一些规范顺序,并且按顺序排序是一个简单的规范顺序。

若要了解如何生成索引,请考虑如何(假设(枚举集合 {1..80} 中六个整数的所有 300,500,200 个组合。首先,我们枚举以 1 开头的组合,然后继续从集合 {2..80} 中获取五个整数。然后,我们枚举以 2 开头的组合,并继续从集合 {3..80} 中获取五个整数。然后,我们枚举以 3 开头的组合,然后继续从集合 {4..80} 中获取五个整数。等等。在每个起点的枚举中,我们递归应用相同的算法。

现在,让我们把这个枚举颠倒过来。假设我们有一些六胞胎{a,b,c,d,e,f}.让我们问一下,在那个六胞胎之后有多少个六胞胎?

  • 首先,所有以大于 a 的值开头的六胞胎。由于六胞胎是有序的,如果一个六胞胎以大于a的值开头,那么它的所有值都大于a,这意味着它是集合{a+1..80}中六个值的某种组合,其中有80-aC6

  • 然后,有所有六胞胎,它们以a开头,然后以第一个值大于 b 的五胞胎继续。根据上述逻辑的相同逻辑,这种六胞胎的数量为80-bC5

  • 然后,有所有六胞胎,它们以a,b开始,然后是第一个值大于c的四重奏:总共80-cC4

  • 等。

因此,{a,b,c,d,e,f}之后的六胞胎总数精确地为:

80-aC6+80-b C5+80-cC 4+80-d C3+80-e C2+80-fC 1

上述等式的有趣之处在于,六个变量之间没有相互作用。我们可以通过为 80-x Ci 的值创建六个查找表来计算该值,为 x 的值从 1 到 80,i的值从 1 到 6。然后,我们可以通过进行六个查找并将值相加来计算任何六胞胎的(逆(索引。(如果需要,我们可以从组合总数中减去反索引以获得正向索引。但在这种情况下,我们所需要的只是从组合到整数的双射,逆索引就可以正常工作。

在算法结束时,有必要将计数指数重新转换为六胞胎。这可以通过执行一系列二叉搜索来使用相同的查找表来完成:首先,通过在查找表中查找 i==6 的二叉搜索来查找 a 的值,然后减去相应的索引并继续在查找表中搜索 i==5 的余数,依此类推(实际上,由于查找表很小, 事实证明,线性搜索可能比二叉搜索更快。这可能没有太大区别。