我有一组固定整数. 我想找到一个整数, 大于某个阈值, IE,这将每个剩下的, IE可以写成,使得余数之和最小化。
换句话说,这是我的问题
如果整数有公约数,这个问题就很简单了。但是,如果整数是相对互质的,则不清楚如何解决它。
套装可以在附近,并且每个元素也有数万的值。
我正在考虑用遗传算法(GA)解决它,但它有点慢。我想知道有没有其他方法可以解决这个问题。
我有一组固定整数. 我想找到一个整数, 大于某个阈值, IE,这将每个剩下的, IE可以写成,使得余数之和最小化。
换句话说,这是我的问题
如果整数有公约数,这个问题就很简单了。但是,如果整数是相对互质的,则不清楚如何解决它。
套装可以在附近,并且每个元素也有数万的值。
我正在考虑用遗传算法(GA)解决它,但它有点慢。我想知道有没有其他方法可以解决这个问题。
集合 |S|=N 可以在 10000 左右,每个元素也有数万的值。
它当然看起来很多,但对于计算机来说,这不是那么多。我们可以从蛮力开始并逐步简化它以获得更好的性能:
您可以尝试从 D>T 到 D<max(S) 的每个整数并存储最佳候选者。
# Fully functional python solution:
import random
import time
# Let's start initializing a random T
T = random.randint(1, 100)
# And a S, with 10,000 integers fom T to 10,000
S = [random.randint(T, 10000) for i in range(10000)]
def LowestRemainder(List, Threshold):
# Storing the all time lowest D and remainder
Best_D_SoFar = Threshold+1
lowestRemainderSoFar = sum(List)
for d in range(Threshold+1, max(List)+1):
remainder = sum(c%d for c in List)
if remainder < lowestRemainderSoFar:
lowestRemainderSoFar = remainder
Best_D_SoFar = d
return Best_D_SoFar, lowestRemainderSoFar
print("Looking for D > %d, that minimizes the sum of all remainder in a list containing %d integers" % (T, len(S)))
# Keep track of time
start_time = time.time()
# call the function
D, R = LowestRemainder(S, T)
# stop the clock
elapsed_time = time.time() - start_time
print("Found D =", D, "and lowest remainder =", R, "in", elapsed_time, "seconds")
我在我的机器上测试了几次,它总是在不到4 秒的时间内运行(最长
但这是蛮力基线。因此,让我们通过提前中断内部循环来提高效率:
# Remove this:
# remainder = sum(c%d for c in List)
# Add this instead:
remainder = 0
for c in List:
remainder += c%d
if remainder > lowestRemainderSoFar:
break
并做了!
(对于整个 10,000 大小的数组,随机整数高达 10,000:)
由于最好的解决方案可能是一个较小的数字(如稍后讨论的那样),如果完成时间太长,您可以简单地中断,您仍然会有一个很好的解决方案(不保证是最佳的),就像 GA 一样(并且可能更好,因为您将快速利用最佳候选人,而不是探索空间)。
假设一个解决方案D,得分为S。
现在分解解,找到构成 D: [p1,p2,…] 的素数列表。
现在删除一些 p,生成一个D',你会注意到c mod(D) >= c mod(D')
对于任何 c、任何 D 和任何 p。
这意味着,如果 D 已经被评估,您不需要探索 D 的任何倍数。
这将大大减少试验量!
使用这个因式分解原理,在 T=1 (如果 T<1,比 D=1 是最佳解)的更简单场景中,我们保证解是素数!此外,您不需要搜索到最大的数字max(S)
,因为sqrt(max(S))
就足够了。
它使编程变得有点困难,但您仍然只需要遍历共同素数。
一旦你击中了大于最小 S ( d>min(S)
) 的第一个候选者 (d),那么这个余数将是 min(S)。
如果目前最好的解决方案已经优于 min(S),我们可以跳过所有下一个候选者。
这个逻辑是累积的。一旦候选 d 大于 N 个最低的 S,余数之和将至少是所有 N 个最低 S 的总和。因此,我们的循环可能类似于:
if(sum([s<d for s in S]) > lowest):
return d
else:
d = next_coprime()
在包含数千个元素的列表中,极有可能尽早找到最小的元素。并且即使它不满足完成条件,它也只会随着 d 的增长而积累更多的元素。
从基线开始并限制边界,直到您的代码在所需的时间运行。
输入:
过程:测试所有d>T,跳过倍数并停在上限: