繁体   English   中英

如何从给定范围计算一个不能被数组的任何元素整除的数字?

[英]How do I count from a given range a number that is not divisible by any element of the array?

问题陈述:

在 1 到 1000000000000000000 之间,除了给定数字的倍数之外,还有多少数字可以快速找到并使用 memory?

例如,1 到 1000000000000000000 之间有多少数字不是[29516, 15, 63315, 94, 43, 77, 33422, 84834, 43803, 61310] 如果您能给我 python 中的代码,将会很有帮助。

# Python3 program for the above approach
 
# Function to find the non-multiples
# till k
def findNonMultiples(arr, n, k):
 
    # Stores all unique multiples
    multiples = set([])
 
    # Iterate the array
    for i in range(n):
 
        # For finding duplicates
        # only once
        if (arr[i] not in multiples):
 
            # Inserting all multiples
            # into the set
            for j in range(1, k // arr[i] + 1):
                multiples.add(arr[i] * j)
 
    # Returning only the count of
    # numbers that are not divisible
    # by any of the array elements
    return k - len(multiples)
 
# Function to count the total values
# in the range [L, R]
def countValues(arr, N, L, R):
 
    # Count all values in the range
    # using exclusion principle
    return (findNonMultiples(arr, N, R) -
            findNonMultiples(arr, N, L - 1))
 
# Driver Code
if __name__ == "__main__":
   
    arr = [ 29516,15,63315,94,43,77,33422,84834,43803,61310 ]
    N = len(arr)
    L = 1
    R = 1000000000000000000
     
    # Function Call
    print( countValues(arr, N, L, R))
 
# This code is contributed by chitranayal

这段代码太慢了,使用了很多memory。 我该如何改进它?

这是一种我没有试图证明的数学猜想方法。 我不保证得到的结果是完全正确的。

我的想法是把 select 的值从给定的列表中一个一个地从范围中移除,并计算每次要移除的数字个数。 根据OP提供的例子,先从10**18中去掉29516的倍数,去掉的个数为:

>>> hi = 10 ** 18
>>> hi // 29516
33879929529746

第二次删除 15。 考虑到15和29516的最小公倍数是442740,我们需要去掉下面的数:

>>> hi // 15 - hi // 442740
66664408004698017

第三次去掉65,要去掉的数字是:

>>> hi // 63315 - hi // lcm(29516, 63315) - hi // lcm(15, 63315) + hi // lcm(29516, 15, 63315)
0

这里 lcm 用于求最小公倍数。

示例到此结束。 这是我的猜测:假设已经删除的号码列表是nums ,当前要删除的号码是num 需要去除的数字数量用Python代码表示为:

hi // num
- sum(hi // lcm(*comb, num) for comb in combinations(nums, 1))
+ sum(hi // lcm(*comb, num) for comb in combinations(nums, 2))
- ...
+/- sum(hi // lcm(*comb, num) for comb in combinations(nums, len(nums)))

以下是实现代码:

from math import lcm
from itertools import combinations


def num_removed(num, nums, hi):
    for i in range(0, len(nums) + 1, 2):
        yield sum(hi // lcm(*comb, num) for comb in combinations(nums, i))
    for i in range(1, len(nums) + 1, 2):
        yield -sum(hi // lcm(*comb, num) for comb in combinations(nums, i))


def main(nums, hi):
    return hi - sum(sum(num_removed(num, nums[:i], hi)) for i, num in enumerate(nums))

计算结果:

>>> main([29516, 15, 63315, 94, 43, 77, 33422, 84834, 43803, 61310], 10 ** 18)
890153441245772172

上面的猜想是基于下界为1的特殊情况。如果下界大于1,一个简单的实现方法是计算两次要去除的数字的个数,然后计算它们的差:

def num_is_multiples(nums, hi):
    return sum(sum(num_removed(num, nums[:i], hi)) for i, num in enumerate(nums))


def num_non_multiples(nums, lo, hi):
    return hi - lo + 1 - (num_is_multiples(nums, hi) - num_is_multiples(nums, lo - 1))

计算结果:

>>> num_non_multiples([29516, 15, 63315, 94, 43, 77, 33422, 84834, 43803, 61310], 200, 10 ** 18)
890153441245771994

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM