【问题标题】:Unstable results from Python factoring functionPython因式分解函数的结果不稳定
【发布时间】:2013-01-29 21:30:00
【问题描述】:
def test_prime(n):
    q = True
    for p in range(2,n):  #Only need to check up to rootn for primes and n/2 for factors
        if int(n%p) is 0:         
            q = False
            print(p, 'and', int(n/p), 'are factors of ', n)    
    if q:
        print(n, 'IS a prime number!')
    else:
        print(n, 'IS NOT a prime number')

我刚刚开始玩 Python,我正在整理一些零碎的东西来打发时间。我一直在尝试测试素数,并有显示非素数因子的想法。我在上面组合的函数似乎运行良好,除了输出不一致。

例如如果我设置 n = 65432 我得到...

2 and 32716 are factors of  65432
4 and 16358 are factors of  65432
8 and 8179 are factors of  65432
8179 and 8 are factors of  65432
16358 and 4 are factors of  65432
32716 and 2 are factors of  65432
65432 IS NOT a prime number

这是我所期望的。但是如果我设置 n = 659306 我会得到......

2 and 329653 are factors of  659306
71 and 9286 are factors of  659306
142 and 4643 are factors of  659306
4643 and 142 are factors of  659306
9286 and 71 are factors of  659306
659306 IS NOT a prime number

这是不同的,因为它最后不包括因子 329653。这不是问题,因为所有因素都显示在某处,但令我烦恼的是,我不知道为什么某些数字会发生这种情况!

只是为了告诉你我不是一个完全的白痴,我发现这似乎只发生在长度超过 5 个字符的整数值上。有人可以告诉我为什么这两种情况下的输出不同吗?

【问题讨论】:

  • 在您的代码中,我看到一个 range[2,n](不是 n/2),以及一条评论说“...将测试到 n/2”。如果你这样做了,那么你会得到你的输出。但是您编写的代码应该按规定工作。不过,您最多需要测试 n/2+1。
  • 不要使用 is 来测试数字相等性。请改用 ==。
  • 是的,我试过玩那条线。这就是为什么它目前计入 n 的原因。如果我输入 for p in range(2,int((n/2)+1)): 那么我会得到相同的结果。
  • 啊哈。 == 给出相同的结果。那么就这样整理了!但我仍然不明白为什么它适用于较小的值?
  • 很有趣,因为我也使用is 得到了“正确”的结果。它必须与机器整数大小有关:我在 64 位 Linux 上运行,@DSM 的测试在我点击0x8000000000000000 之前不会返回False。有趣的是,您应该在这么小的数字上出错;除非您使用无符号 16 位整数,否则您的限制是 65535?

标签: python factorization


【解决方案1】:

你想要n % p == 0,而不是n % p is 0is 测试同一性,而不是相等性,并且不是每个 0 与其他每个 0 相同。

>>> 659306 % 329653
0
>>> (659306 % 329653) == 0
True
>>> (659306 % 329653) is 0
False
>>> id(0)
136748976
>>> id(659306 % 329653) 
3070888160

那里的id基本上对应内存中的一个位置。

这样想:如果你有一个加元,我有一个加元,那么它们的价值是相等的(1 == 1),但它们不是同一个对象(我的一美元硬币与您的一美元硬币不同。)我们可以共享同一个硬币,但我们没有必要这样做。

[PS:整数除法可以使用n//p代替int(n/p)。]

【讨论】:

  • 正确的钱。如果数字足够小,它是一个整数,它的余数也是一个整数,如果为零,那就是0。如果数字太大,它被提升为long,它的余数也是,如果为零它仍然很长,那是0L,它 0相同。
  • 太棒了。你们都是很棒的人——我学到了一些关于计算的有趣的东西!非常感谢,现在一切都说得通了。我要添加一些代码来阻止机器在明显找到所有因素后继续工作!
  • @user2023223:很高兴我们能提供帮助。通过像您一样给我们一个SSCCE,您可以非常轻松地验证答案。如果你保持这个习惯,你会在这里做得很好。 :^)
  • @lserni:通常可能有多个相同类型和值的对象实例(由== 运算符定义)。尽管在 CPython 中缓存了小整数(可能还有文字),因此有一个 0 类型为 int
  • 我在 Python 3 上尝试过 (65930600000000000000000000 % 32965300000000000000000000) is 0,其中没有 intlong 的区别,它仍然会产生 False
【解决方案2】:

幕后发生的事情有点复杂。我的 cmets 特别适用于CPython。 PyPy、Jython、IronPython 等其他实现的行为会有所不同。

为了减少内存使用并提高性能,CPython 缓存了一系列小整数并尝试返回对这些对象的引用,而不是创建另一个具有相同值的整数对象。当您将数字与is 进行比较时,您实际上是在检查 CPython 是否返回了对同一缓存对象的引用。但有时 CPython 不会检查一个值是否是缓存整数之一。怎么会这样?

我将解释 CPython 3,因为它比 CPython 2 简单一点。CPython 中可见的int 类型实际上在解释器内部称为PyLongPyLong 将整数存储为 digits 的数组,其中每个数字介于 0 和 2**15-1(32 位系统)或 0 和 2**30-1(64 位系统)之间。随着数字变大,数组的大小也会增加;这允许有效地无限整数。在计算 % 时,CPython 会检查第二个参数是否为一个 digit 长。如果是这样,它会调用一个 C 函数 (divrem1),该函数返回一个 digit 作为结果。接下来,调用PyLong_FromLong 将适合C long 的值(即divrem 的返回值)转换为PyLong。 PyLong_FromLong 检查参数是否在缓存整数范围内,如果可能,将返回对缓存整数的引用。

如果第二个参数的长度超过一个 digit,则调用不同的 C 函数 (x_divrem)。 x_divrem 使用通用的任意精度除法算法来计算余数。由于x_divrem 在计算过程中创建了一个PyLong 来存储余数,因此避免创建另一个重复整数没有任何好处;它已经存在。对于随机大数的计算,余数很少会等于缓存的整数之一,因此不值得花时间进行检查。

还有其他方法可以创建缓存整数的重复副本。我只是分析了问题中的那个。

这就是为什么你不使用is 来检查数值相等.....

【讨论】:

  • @DSM 我更新了使用 CPython 的答案,并注意其他实现的行为不同。
猜你喜欢
  • 2013-01-19
  • 2013-12-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-19
  • 1970-01-01
  • 2018-08-07
  • 2018-03-17
相关资源
最近更新 更多