【问题标题】:Finding numbers from a to b not divisible by x to y查找从 a 到 b 不能被 x 到 y 整除的数字
【发布时间】:2013-04-12 05:14:18
【问题描述】:

这是我思考了很久的问题。

找出从 a 到 b 的所有数不能被从 x 到 y 的任何数整除的最快方法是什么?

考虑一下:

我想找出 1 到 10 中所有不能被 2 到 5 整除的数字。 如果我在哪里使用线性方法,这个过程将变得非常缓慢; 像这样:

result = []
a = 1
b = 10
x = 2
y = 5
for i in range(a,b):
    t = False
    for j in range(x,y):
        if i%j==0:
            t = True
            break
    if t is False:
        result.append(i)
return result

有谁知道比线性解决方案计算时间更短的任何其他方法?

如果没有,谁能看到这个可能做得更快,因为我现在是空白的......

真诚地, 约翰

[编辑]

数字的范围是0到>1,e+100

对于 a、b、x 和 y 都是如此

【问题讨论】:

  • 您是针对大 (b-a)、大 b、大 (y-x)、大 y 优化还是使用小数字多次调用它?我怀疑答案会因这些问题而异
  • 这是问题的一部分:a、b、x、y,逐渐增长
  • 你不想写 1e100 而不是 "1,e+100" 吗?如果是这种情况,那么很难找到一种非常快速的方法,因为这组数字不适合内存,甚至是硬盘驱动器(到目前为止)。如果数字计数是合理的(比如大约 1e8,以便它们适合内存),那么可以通过以内存换速度来获得快速的方法。

标签: python math set division modulo


【解决方案1】:

您只需要检查可能的除数范围内的质数 - 例如,如果一个值不能被 2 整除,那么它也不能被 2 的任何倍数整除;对于其他所有素数和素数倍数也是如此。因此,在您的示例中,您可以检查2, 3, 5 - 您不需要检查4,因为任何能被4整除的东西都必须能被2整除。因此,更快的方法是计算您感兴趣的任何范围内的素数,然后简单地计算它们相除的值。

另一个加速是将您感兴趣的范围内的每个值添加到set:当您发现它可以被您范围内的数字整除时,将其从集合中删除。然后,您应该只测试保留在集合中的数字 - 这将阻止您多次测试数字。

如果我们结合这两种方法,我们可以看到我们可以创建一个包含所有值的 set(因此在示例中,一个包含所有值 1 到 10 的集合),并简单地删除第二个中每个素数的倍数范围从那个集合。

编辑:正如 Patashu 指出的那样,如果除以给定值的素数不在集合中,这将不太有效。为了解决这个问题,我们可以对上面应用类似的算法:创建一个带有值[a, b]set,对于set 中的每个值,删除它的所有倍数。因此,对于下面在 cmets 中给出的示例(使用 [3, 6]),我们将从 3 开始并删除它在集合中的倍数 - 所以 6。因此,我们需要测试的剩余值将是 [3, 4, 5],这就是我们在这种情况下想要的。

Edit2:这是一个非常糟糕的、蹩脚的实现,它没有经过优化并且有可怕的变量名:

def find_non_factors():
    a = 1
    b = 1000000
    x = 200
    y = 1000

    z = [True for p in range(x, y+1)]
    for k, i in enumerate(z):
        if i:
            k += x
            n = 2
            while n * k < y + 1:
                z[(n*k) - x] = False
                n += 1

    k = {p for p in range(a, b+1)}

    for p, v in enumerate(z):
        if v:
            t = p + x
            n = 1
            while n * t < (b + 1):
                if (n * t) in k:
                    k.remove(n * t)
                n += 1

    return k

使用这些数字尝试您的原始实现。在我的电脑上花费 > 1 分钟。此实现需要不到 2 秒的时间。

【讨论】:

  • 这是不正确的,例如 7*11 不能被 2、3、4 或 5 整除,但它也不是素数。
  • @Patashu 你误解了我所说的(虽然我同意我没有说得那么好)。我的意思是,在[2, 5] 的范围内,您只需要测试2, 3, 5 - 测试2 将测试4 和所有其他2 的倍数。同样,在[2, 10] 中测试可分性,您只需检查2, 3, 5, 7
  • 所以你只需要检查它是否可以被素数整除就是他所说的:P
  • @Yuushi 好的,我知道你现在指的是什么质数了。
  • @Yuushi 如果你有 3、4、5、6 怎么办? 4 不是质数,但您需要保留它,因为您没有测试 2。
【解决方案2】:

终极优化警告:不要过早优化。每当您尝试优化代码时,对其进行分析以确保其需要优化,并在您打算对其进行优化的同一类型数据上分析优化以确认它是加速。几乎所有代码都不需要优化,只为给出正确答案。

如果您针对小 x-y 和大 a-b 进行优化:

创建一个长度为所有 x、x+1、x+2...y 中最小公倍数的数组。例如,对于 2、3、4、5,它将是 60,而不是 120。

现在用布尔值填充这个数组 - 最初为每个单元格填充 false,然后为 x-y 中的每个数字,用 true 填充数组中该数字的倍数的所有条目。

现在对于 a-b 中的每个数字,以数组长度为模索引到数组中,如果为真,则跳过,否则如果为假,则返回。

您可以通过从 x 到 y 因子数中删除素数扩展是其他数的素数扩展的严格超集来更快地完成此操作。我的意思是 - 如果你有 2、3、4、5、4 是 2*2 的严格超集 2 所以你可以删除它,现在我们的数组长度只有 30。对于像 3、4、5、6 这样的东西但是,4 是 2*2,6 是 3*2 - 6 是 3 的超集,因此我们将其删除,但 4 不是所有内容的超集,因此我们将其保留。LCM 为 3*2*2*5 = 60 . 做这种事情会为大 a-b 本身提供一些速度,如果你只需要这样做,你可能不需要去阵列方向。

另外,请记住,如果您不打算每次都使用函数的整个结果 - 例如,有时您可能只对最低值感兴趣 - 将其编写为生成器而不是功能。这样你就可以调用它,直到你有足够的号码,然后停止,节省时间。

【讨论】:

  • 感谢您的回复!这比提供的示例要快得多,正如您所说:“优化小 x-y 和大 a-b” 当 x-y 的范围变大时会出现问题。以免出现混淆:您将 x-y 识别为,我将其识别为 a-b。
  • @user2272969 我应该使用与您相同的命名方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-04-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多