快速确定数字是否除以集合中的任何元素

时间:2012-05-08 10:14:00

标签: algorithm math language-agnostic

是否有一种算法可以快速确定数字是否是给定数字集的一个因子?

例如,12[24,33,52]的因子,而5则不是。

是否有比线性搜索O(n)更好的方法?该集将包含几百万个元素。我无需查找号码,只需truefalse结果。

7 个答案:

答案 0 :(得分:1)

如果针对常量列表检查大量数字,则加速该过程的一种可能方法是首先将列表中的数字分解为其素数因子。然后将列表成员放在字典中,并将主要因素作为键。然后,当一个数字(潜在因素)到来时,你首先将其分解为其素因子,然后使用构造的字典来检查该数字是否是可能是给定数字的倍数的数字因子。

答案 1 :(得分:0)

我认为一般来说O(n)搜索是你最终会得到的。但是,根据数字的大小,您可以大大加快搜索速度,假设该集合已经排序(您提到它可以),通过观察如果您正在搜索找到可被D整除的数字并且您有当前扫描的x和x不能被D整除,下一个可能的候选者显然在楼层([x + D] / D)* D.也就是说,如果D = 12并且列表是

5 11 13 19 22 25 27

并且您正在扫描13,下一个可能的候选编号将是24.现在,根据您输入的分布,您可以使用二进制搜索而不是线性搜索向前扫描,因为您现在搜索列表中不小于24的最小数字,并对列表进行排序。如果D很大,那么你可以通过这种方式节省大量的比较。

然而,从纯计算复杂性的角度来看,排序然后搜索将是O(n log n),而线性扫描只是O(n)。

答案 2 :(得分:0)

为了测试针对常数集的许多潜在因素,您应该意识到如果集合中的一个元素只是另外两个元素的倍数,则它是无关紧要的并且可以被删除。这种方法是一种称为Sieve of Eratosthenes的古老算法的变体。在测试大量候选人时,交易启动时间为运行时间:

  1. 在集合
  2. 中选择最小数字> 1
  3. 从集合
  4. 中删除该数字的任何倍数,除了自身
  5. 对于下一个最小数字,重复2次,进行一定次数的迭代。迭代次数取决于与启动时间的权衡
  6. 现在你留下了一个小得多的设置来彻底测试。为了提高效率,您需要一个允许删除O(1)的集合的数据结构,如链接列表,或者只需将“已删除”元素替换为零,然后将非零元素复制到新容器中。 / p>

答案 3 :(得分:0)

我不确定这个问题,所以让我问另一个问题:12是[6,33,52]的因素吗?很明显,12不分6,33或52.但12的因子是2 * 2 * 3,6和33和52的因子是2 * 2 * 2 * 3 * 3 * 11 * 13。 12中的所有因子都存在于集合[6,33,52]中,具有足够的多重性,因此可以说12是[6,33,52]的因子。

如果你说12不是[6,33,52]的因子,那么没有比将每个数字的可分性测试12更好的解决方案了;只需执行除法并检查剩余部分。因此6%12 = 6,33%12 = 9,52%12 = 4,因此12不是[6.33.52]的因子。但是如果你说12 因子[6,33,52],那么确定一个数字 f 是否是一个因子 ns < / em>,只需将数字 ns 依次相乘,每次乘法后取余数模数 f ,如果余数为永远则立即报告 true 如果到达数字列表 ns 的末尾而没有余数为0,则报告 false

我们举两个例子。首先,12是因子[6,33,52]?第一个(平凡的)乘法结果为6,余数为6.现在6 * 33 = 198,除以12给出余数6,我们继续。现在6 * 52 = 312和312/12 = 26r0,所以我们有一个0的余数,结果是 true 。第二,是因子[24,33,52]?乘法链为24%5 = 5,(5 * 33)%5 = 2,(2 * 52)%5 = 4,因此5不是[24,33,52]的因子。

该算法的一种变体最近用于attack RSA密码系统;你可以阅读有关攻击的工作方式here

答案 4 :(得分:0)

由于要搜索的集合是固定的,因此花费时间组织搜索集合花费的任何时间都是固定的。如果你可以在内存中获取该集合,那么我希望二进制树结构适合。平均在二叉树中搜索元素是O(log n)操作。

如果你有理由相信集合中的数字在[0..10^12]范围内均匀分布,那么在内存中对有序集合的二进制搜索应该与搜索二叉树一样。另一方面,如果集合中的中间元素(或集合的任何子集)预计不会接近集合(或子集)所包含的范围中的中间值,那么我认为二叉树将更好(实际)表现。

如果你无法将整个集合放入内存中,那么将其分解为适合内存并将这些块存储在磁盘上的块可能是要走的路。您可以将集合的根分支和上分支存储在内存中,并使用它们索引到磁盘上。保存在内存中的树的部分深度是你应该自己决定的,但是如果你需要超过root和2级分支,我会感到惊讶。在磁盘上提供8个块。

当然,这只能解决部分问题,找出给定数字是否在集合中;你真的想知道给定的数字是否是集合中任何数字的因子。正如我在评论中所建议的那样,我认为任何基于对集合中数字进行分解的方法都是没有希望的,给出了超出多项式时间的预期运行时间。

我会反过来解决问题的这一部分:生成给定数字的倍数并搜索它们中的每一个。如果您的设置包含10^7个元素,那么任何给定的数字N在该集合中将具有大约(10^7)/N个倍数。如果从[0..10^12]范围内随机抽取给定数字,则N的平均值为0.5*10^12,这表明(反直觉地)在大多数情况下您只需要搜索N本身。

是的,我知道在很多情况下你需要搜索更多的值。

这种方法相对容易并行化。

答案 5 :(得分:0)

需要一些预计算的快速解决方案:

使用以下规则在二叉树中整理您的集:

  • 该组的数字在树叶上。
  • 树的根包含r所有素数的最小值,这些素数除以一组数。
  • 左子树对应r的倍数子集(除以r,以便r不会无限重复。“
  • 右侧子树对应的数字子集不是r的倍数。

如果您想测试一个数字N是否划分了该集合的某个元素,请计算其主要分解并遍历树,直到您到达一个叶子。如果叶子包含一个数字,那么N除以它,否则如果叶子为空,则N除以该集合中的任何元素。

答案 6 :(得分:0)

只需计算集合的乘积并使用测试因子修改结果。 在你的例子中 {24,33,52} P = 41184

Tf 12:41184 mod 12 = 0 True Tf 5:41184 mod 5 = 4 False

如果计算产品会溢出计算器的算术,则可以将集合分成块,但是通过存储字符串可以实现大量数据。