通过计算所有成对的gcd,删除重复项然后递归,可以相对有效地解决此问题。这是在重复执行之前删除重复项的操作,这样可以提高效率。
我将在下面更详细地说明该算法,但首先,它有助于定义二进制运算符。如果是正整数集,则定义小号,Ť⊗小号,Ť
小号⊗ Ť= { GCD (小号,吨):小号∈ 小号,吨∈ Ť}.
注意和(在您的问题中); 通常,甚至会小于上述两个界限中的任何一个,这有助于提高算法的效率。还要注意,我们可以用计算通过简单的枚举进行gcd操作。| 小号⊗ Ť | ≤ 10 9小号⊗ Ť 小号⊗ Ť | S | × | T ||S⊗T|≤|S|×|T||S⊗T|≤109S⊗TS⊗T|S|×|T|
使用该符号,这里是算法。令为数字输入集。计算,然后计算,然后,依此类推。找到最小使得但。然后,您知道最小的此类子集的大小为。如果您还想输出此类子集的具体示例,则通过保留反向指针,可以轻松地重构此类集。小号2 = 小号1 ⊗ 小号1 小号3 = 小号1 ⊗ 小号2 小号4 = 小号1 ⊗ 小号3 ķ 1 ∈ 小号ķ 1 ∉ 小号ķ - 1 ķS1S2=S1⊗S1S3=S1⊗S2S4=S1⊗S3k1∈Sk1∉Sk−1k
这将是相对有效的,因为中间集的大小都不会增长到以上(实际上,它们的大小可能会小得多),并且运行时间大约需要 gcd操作。 500 × (| S 1 | + | S 2 | + ⋯ )109500×(|S1|+|S2|+⋯)
这是一种优化,可以进一步提高效率。基本上,您可以使用迭代加倍来找到最小的,使得中的。特别是,对于每个元素,我们跟踪的最集,其gcd为,大小为。(当删除重复项时,您将较小的子集来解决关系。)现在,与其计算九个集合的序列计算五个集合的序列,通过计算,然后,然后1 ∈ 小号ķ X ∈ 小号我š 1 X ≤ 我š 1,s ^ 2,S ^ 3,s ^ 4,... ,s ^ 9 s ^ 1,S ^ 2,S ^ 4,s ^ 8,š 9 š 2 = s ^ 1 ⊗ 小号1 小号4 = 小号2 ⊗ 小号2 小号k1∈Skx∈SiS1x≤iS1,S2,S3,S4,…,S9S1,S2,S4,S8,S9S2=S1⊗S1S4=S2⊗S2小号9 = 小号1 × 小号8 ķ ∈ [ 1 ,2 ,4 ,8 ,9 ] 1 ∈ 小号ķ ķ 1 ∈ 小号ķ 1 1 小号ķ 1 ∈ 小号ķS8=S4⊗S4,然后。转到时,找到第一个使得。一旦你找到使得,您可以立即停止:你能找到的最小的子集,其最大公因数通过查看相关联的子集。因此,您可以在达到设置为中的立即停止,如果找到较小的子集,则可以尽早停止。S9=S1×S8k∈[1,2,4,8,9]1∈Skk1∈Sk11Sk1∈Sk
这应该是省时和省空间的。为了节省空间,对于每个元素,您都不需要存储整个集合:足以存储两个反向指针(因此,您将gcd的的两个元素用于获取)和可选地,相应子集的大小。小号我,小号Ĵ Xx∈SkSi,Sjx
原则上,您可以用任何其他加法链替换序列。我不知道其他加法链是否会更好。最佳选择可能取决于正确答案的分布以及集合的预期大小,这对我来说并不明确,但可能可以通过实验根据经验得出。小号ķ[ 1 ,2 ,4 ,8 ,9 ]小号ķ
鸣谢:感谢KWillets提出了将数字子集与每个元素一起存储的想法,该想法允许提前停止。小号一世