如何计算整数的倒数之和?

How to calculate sum of reciprocals with integers?

我想计算一个整数列表的倒数之和(看看它是否大于或等于 1):

我想使用整数来避免 floating-point rounding issues。为此,我想这样解决:

我已经这样做了:

import numpy as np

my_list = [2, 3, 5, 7]
numerator = 0
for i in range(len(my_list)):
    numerator += np.product(my_list[:i] + my_list[i+1 :])
denominator = np.product(my_list)
result = numerator>=denominator

但我觉得应该有一条线。有没有函数可以将倒数之和计算为分数?或者也许是一个从列表中计算分子的函数?

Fraction 类型可以轻松准确地做到这一点:

>>> from fractions import Fraction
>>> bottoms = [2, 3, 5, 7]
>>> total = sum(Fraction(1, d) for d in bottoms)
>>> total
Fraction(247, 210)
>>> total > 1
True

如果您想通过 cross-multiplying 个整数而不使用除法来执行此操作,您可以使用 itertools.combinations() 函数。

from itertools import combinations

def product(iterable):
    prod = 1
    for item in iterable: prod *= item
    return prod

my_list = [2, 3, 5, 7]
n = len(my_list)

numerator = sum(product(combo) for combo in combinations(my_list, n-1))
denominator = product(my_list)

然后,您可以比较 numerator >= denominator 而不是 fraction >= 1

请注意,我定义了 product() 函数来给出可迭代对象的乘积,其方式与 sum() 给出可迭代对象元素之和的方式相同。

使用 math.prod 并通过将数字除以分母来计算分子:

den = prod(my_list)
num = sum(den // i for i in my_list)
print(num >= den)

具有从 2 到 8000 的 1000 个随机整数的三个列表的基准测试(看起来总和达到 1 的概率约为 50%):

 36.4 ms   35.5 ms   34.8 ms  Tim_Fractions
333.9 ms  322.5 ms  326.3 ms  Pranav_Combinations
  6.0 ms    5.9 ms    5.9 ms  Kelly_Divide

以最多8000个素数为基准(只是因为你的例子做了这样的事情,虽然1/2+1/3+1/5已经超过了1):

123.9 ms  123.8 ms  126.0 ms  Tim_Fractions
304.4 ms  313.6 ms  298.2 ms  Pranav_Combinations
  5.9 ms    5.9 ms    5.9 ms  Kelly_Divide

如果你坚持要单线:

(d := prod(my_list)) <= sum(d // i for i in my_list)

可能的优化思路:对数字进行排序,不要盲目求和,一到1就停止

基准代码:

def Tim_Fractions(bottoms):
    return sum(Fraction(1, d) for d in bottoms) >= 1

def Pranav_Combinations(my_list):
    def product(iterable):
        prod = 1
        for item in iterable: prod *= item
        return prod
    n = len(my_list)
    numerator = sum(product(combo) for combo in combinations(my_list, n-1))
    denominator = product(my_list)    
    return numerator >= denominator

def Kelly_Divide(my_list):
    den = prod(my_list)
    num = sum(den // i for i in my_list)
    return num >= den

funcs = [
    Tim_Fractions,
    Pranav_Combinations,
    Kelly_Divide,
]

from timeit import repeat
from fractions import Fraction
from itertools import combinations
from math import prod
import random

my_list = [i for i in range(2, 8000) if all(i % d for d in range(2, i))]
tss = [[] for _ in funcs]
for _ in range(3):
    # remove the next line if you want to benchmark with the primes instead
    my_list = random.sample(range(2, 8000), 1000)
    for func, ts in zip(funcs, tss):
        t = min(repeat(lambda: func(my_list), number=1))
        ts.append(t)
        print(*('%5.1f ms ' % (t * 1e3) for t in ts), func.__name__)
    print()