用于查找满足约束的列表中的子集的算法

时间:2017-02-25 19:50:37

标签: python algorithm math graph tree

我正在寻找一种有效的算法来执行以下任务。 python中的实现是最佳的,但使用另一种语言或只是伪代码也会有所帮助。

给定是有理数 x 和大约600个有理数的列表。 在该列表中,我想找到满足约束条件的10个数字(无重复)的子集

s = x的总和以及子集中数字的总和

使 s 与最接近的整数之间的绝对差值小于10e-12。

我想这可能是通过某种图形算法完成的,但我不知道该怎么做。

示例:这是一种天真的方法来说明我在寻找什么。显然,这种方法对于由列表和子集产生的可能组合的数量来说效率低,如说明书中给出的那样大:

#!/bin/python3

import sys
from math import pow
from itertools import combinations

x = 0.5
list = [1.000123,2.192,3.2143124,1.00041,2.0043,3.5]
for c in combinations(list,3):
    s = x + sum(c)
    r = s%1    
    if r <= pow(10,-3) or r >= 1-pow(10,-3):
        print("Found combination: s=%s for %s"%(s,c))
        break

示例输出:

  

找到组合:s = 6.000533(1.000123,1.00041,3.5)

3 个答案:

答案 0 :(得分:4)

这个问题可以表述为一个数学程序,它使我们能够用专门的算法解决它,比如单纯形算法和分支定界。在大多数情况下,使用分支和绑定,我们只需要查看搜索树的一小部分即可找到最佳解决方案。

在Python中有许多用于制定和解决混合整数程序的库。我认为CyLPPuLP是众所周知的免费赠品。

easy_install pulp

如果你很容易安装,PuLP会附带一个解决整数问题的免费求解器,所以我建议使用PuLP,如果它足以解决你的问题。

以下是PuLP问题的实现:

from pulp import *

items = [1.000123,2.192,3.2143124,1.00041,2.0043,3.5]
x = 0.5
tol = 0.001
numstouse = 3

delta = pulp.LpVariable.dicts('listitems', range(len(items)), lowBound = 0, upBound = 1, cat = pulp.LpInteger)
s = pulp.LpVariable('value', lowBound = 0, cat = pulp.LpInteger)
r = pulp.LpVariable('deviation', lowBound = -tol, upBound=tol)

# Model formulation
prob = LpProblem("Sum Problem", LpMinimize)

# Constraints
prob += lpSum([delta[i] * items[i] for i in range(len(items))]) + x + r == s
prob += lpSum([delta[i] for i in range(len(items))]) == numstouse
prob.solve()
print("Found combination: s={} for {}".format(s.value(), tuple(items[i] for i in range(len(items)) if delta[i].value() == 1)))
if LpStatus[prob.status] == "Optimal":
    for i in delta.keys():
        if delta[i].value() == 1:
            print("List item {} is used in the sum with value {}.".format(i, items[i]))
else:
    print("The problem seems to be infeasible.")

重要的是要注意,尽管PuLP最终将使用默认的自由求解器商业求解器are many many times faster than free ones来解决您的问题。随着问题规模的扩大,自由求解器可能比商业求解器慢。

答案 1 :(得分:2)

这是一个仅使用numpy的启发式解决方案。

更新:请原谅我的吹牛,但请务必不要错过这篇文章的最后部分,我们在10 ^ -12精度的N = 1200数字中求解k = 80项。解算器在适度的硬件上在4秒内找到100多个解决方案。

算法(对于N = 600,k = 10,eps = 10 ^ -12情况):

它利用统计上有很多很多解决方案,并且只采样一个可管理的子空间。

实际上,如果样品均匀分布,则足以随机测试4×10 ^ 12个总和,以找到解决方案的概率> 99.9%。这可以通过分成两组2x10 ^ 6 halfsum来达到易处理的水平,因为可以使用仅涉及对4x10 ^ 6数字进行排序的技巧来避免计算大多数成对和,这可以在当前硬件上轻松完成。 / p>

这是它的工作原理。

它选择10个不相交的随机子样本30(可以下降到20并且仍然非常确定找到解决方案),将它们分成两部分并计算每一半的所有30 ** 5总和。然后从减去输入x减去一半。然后将所有内容以模1减少并排序。

连续元素之间的差异通常是低于10 ^ -12容差的2,000,其中一半是来自不同半部分的总和。所有这些都是解决方案。

代码的大多数复杂性都归咎于追溯间接排序。

import numpy as np
import time

def binom(N, k):
    return np.prod(np.arange(N, N-k, -1).astype(object)) \
        // np.prod(np.arange(2, k+1).astype(object))

def master(nlist, input, k=10, HS=10**7, eps=12, ntrials=10):
    for j in range(ntrials):
        res = trial(nlist, input, k=k, HS=HS, eps=eps)
        if not res is None:
            return res
     print("No solution found in", ntrials, "trials.")

def trial(nlist, input, k=10, HS=10**7, eps=12):
    tol = 10**-eps
    srps = str(eps)
    t0 = time.time()
    N = len(nlist)
    if 2**(k//2) > HS or k > 64:
        kk = min(2 * int (np.log(HS) / np.log(2)), 64)
    else:
        kk = k        
    kA, kB = (kk+1)//2, kk//2
    CA = min(int(HS**(1/kA)), (N+kk-k) // (kA+kB))
    CB = min(int(HS**(1/kB)), (N+kk-k) // (kA+kB))
    inds = np.random.permutation(N)
    indsA = np.reshape(inds[:kA*CA], (kA, CA))
    indsB = np.reshape(inds[kA*CA:kA*CA+kB*CB], (kB, CB))
    extra = inds[N-k+kk:]
    A = sum(np.ix_(*tuple(nlist[indsA]))).ravel() % 1
    B = (-input - nlist[extra].sum()
         - sum(np.ix_(*tuple(nlist[indsB]))).ravel()) % 1
    AB = np.r_[A, B]
    ABi = np.argsort(AB)
    AB = np.where(np.diff(AB[ABi]) < tol)[0]
    nsol = len(AB)
    if nsol == 0:
        return None
     # translate back ...
    ABl = ABi[AB]
    ABh = ABi[AB+1]
    ABv = (ABl >= CA**kA) != (ABh >= CA**kA)
    nsol = np.count_nonzero(ABv)
    if nsol == 0:
        return None
    ABl, ABh = ABl[ABv], ABh[ABv]
    Ai = np.where(ABh >= CA**kA, ABl, ABh)
    Bi = np.where(ABh < CA**kA, ABl, ABh) - CA**kA
    Ai = np.unravel_index(Ai, kA * (CA,))
    Bi = np.unravel_index(Bi, kB * (CB,))
    solutions = [np.r_[indsA[np.arange(kA), Aii],
                       indsB[np.arange(kB), Bii], extra]
                 for Aii, Bii in zip(np.c_[Ai], np.c_[Bi])]
    total_time = time.time() - t0
    for sol in solutions:
        print(("{:."+srps+"f}  =  {:."+srps+"f}  " + "\n".join([
            j * (" + {:."+srps+"f}") for j
            in np.diff(np.r_[0, np.arange(4, k, 6), k])])).format(
                   nlist[sol].sum() + input, input, *nlist[sol]))
    print("\n{} solutions found in {:.3f} seconds, sampling {:.6g}% of"
          " available space.".format(nsol, total_time,
                                     100 * (CA**kA + CB**kB) / binom(N, k)))
    return solutions

输出:

a = np.random.random(600)
b = np.random.random()
s = trial(a, b)
...
<  --   snip   --  >
...
5.000000000000  =  0.103229509601  + 0.006642137376 + 0.312241735755 + 0.784266426461 + 0.902345822935 + 0.988978878589
 + 0.973861938944 + 0.191460799437 + 0.131957251738 + 0.010524218878 + 0.594491280285
5.999999999999  =  0.103229509601  + 0.750882954181 + 0.365709602773 + 0.421458098864 + 0.767072742224 + 0.689495123832
 + 0.654006237725 + 0.418856927051 + 0.892913889958 + 0.279342774349 + 0.657032139442
6.000000000000  =  0.103229509601  + 0.765785564962 + 0.440313432133 + 0.987713329856 + 0.785837107607 + 0.018125214584
 + 0.742834214592 + 0.820268051141 + 0.232822918386 + 0.446038517697 + 0.657032139442
5.000000000001  =  0.103229509601  + 0.748677981958 + 0.708845535002 + 0.330115345473 + 0.660387831821 + 0.549772082712
 + 0.215300958403 + 0.820268051141 + 0.258387204727 + 0.010524218878 + 0.594491280285
5.000000000001  =  0.103229509601  + 0.085365104308 + 0.465618675355 + 0.197311784789 + 0.656004057436 + 0.595032922699
 + 0.698000899403 + 0.546925212167 + 0.844915369567 + 0.333326991548 + 0.474269473129

1163 solutions found in 18.431 seconds, sampling 0.000038% of available space.

由于仅使用简单操作,因此我们基本上仅受浮点精度的限制。所以我们要求10 ^ -14:

...
<  --   snip   --  >
...
6.00000000000000  =  0.25035941161807  + 0.97389388071258 + 0.10625051346950 + 0.59833873712725 + 0.89897827417947
 + 0.78865856416474 + 0.35381392162358 + 0.87346871541364 + 0.53658653353249 + 0.21248261924724 + 0.40716882891145
5.00000000000000  =  0.25035941161807  + 0.24071288846314 + 0.48554094441439 + 0.50713200488770 + 0.38874292843933
 + 0.86313933327877 + 0.90048328572856 + 0.49027844783527 + 0.23879340585229 + 0.10277432242557 + 0.53204302705691
5.00000000000000  =  0.25035941161807  + 0.38097649901116 + 0.48554094441439 + 0.46441170824601 + 0.62826547862002
 + 0.86313933327877 + 0.33939826575779 + 0.73873418282621 + 0.04398883198337 + 0.62252491844691 + 0.18266042579730
3.00000000000000  =  0.25035941161807  + 0.06822167273996 + 0.23678340695986 + 0.46441170824601 + 0.08855356615846
 + 0.00679943782685 + 0.74823208211878 + 0.56709685813503 + 0.44549706663049 + 0.05232395855097 + 0.07172083101554
4.99999999999999  =  0.25035941161807  + 0.02276077008953 + 0.29734365315824 + 0.74952397467956 + 0.74651313615300
 + 0.06942795892486 + 0.33939826575779 + 0.28515053127059 + 0.75198496353405 + 0.95549430775741 + 0.53204302705691
6.00000000000000  =  0.25035941161807  + 0.87635507011986 + 0.24113470302798 + 0.37942029808604 + 0.08855356615846
 + 0.30383588785334 + 0.79224372764376 + 0.85138208150978 + 0.76217062127440 + 0.76040834996762 + 0.69413628274069
5.00000000000000  =  0.25035941161807  + 0.06822167273996 + 0.51540640390940 + 0.91798512102932 + 0.63568890016512
 + 0.75300966489960 + 0.30826232152132 + 0.54179156374890 + 0.30349257203507 + 0.63406153731771 + 0.07172083101554

11 solutions found in 18.397 seconds, sampling 0.000038% of available space.

或者我们可以减少样本数量,以缩短执行时间:

...
<  --   snip   -- >
...
4.999999999999  =  0.096738768432  + 0.311969906774 + 0.830155028676 + 0.164375548024 + 0.118447437942
 + 0.362452121111 + 0.676458354204 + 0.627931895727 + 0.568131437959 + 0.579341106837 + 0.663998394313
5.000000000000  =  0.096738768432  + 0.682823940439 + 0.768308425728 + 0.290242415733 + 0.303087635772
 + 0.776829608333 + 0.229947280121 + 0.189745700730 + 0.469824524584 + 0.795706660727 + 0.396745039400
6.000000000000  =  0.096738768432  + 0.682823940439 + 0.219502575013 + 0.164375548024 + 0.853518966685
 + 0.904544718964 + 0.272487275000 + 0.908201512199 + 0.570219149773 + 0.840338947058 + 0.487248598411
6.000000000001  =  0.096738768432  + 0.838905554517 + 0.837179741796 + 0.655925596548 + 0.121227619542
 + 0.393276631434 + 0.529706372738 + 0.627931895727 + 0.857852927706 + 0.827365021028 + 0.213889870533
5.000000000000  =  0.096738768432  + 0.037789824744 + 0.219502575013 + 0.578848374222 + 0.618570311975
 + 0.393356108716 + 0.999687645216 + 0.163539900985 + 0.734447052985 + 0.840338947058 + 0.317180490652
5.000000000001  =  0.096738768432  + 0.093352607179 + 0.600306836676 + 0.914256455483 + 0.618570311975
 + 0.759417445766 + 0.252660056506 + 0.422864494209 + 0.298221673761 + 0.456362751604 + 0.487248598411

25 solutions found in 1.606 seconds, sampling 0.000001% of available space.

最后,它很容易扩展:

N = 1200; a = np.random.random(N)
b = np.random.random()
k = 80; s = nt6.trial(a, b, k)

输出:

...
<  --   snip   --  >
...
37.000000000000  =  0.189587827991   + 0.219870655535 + 0.422462560363 + 0.446529942912 + 0.340513300967
 + 0.272272603670 + 0.701821613150 + 0.016414376458 + 0.228845802410 + 0.071882553217 + 0.966675626054
 + 0.947578041095 + 0.016404068780 + 0.010927217220 + 0.160372498474 + 0.498852167218 + 0.018622555121
 + 0.199963779290 + 0.977205343235 + 0.272323870374 + 0.468492667326 + 0.405511314584 + 0.091160625930
 + 0.243752782720 + 0.563265391730 + 0.938591630157 + 0.053376502849 + 0.176084585660 + 0.212015784524
 + 0.093291552095 + 0.272949310717 + 0.697415829563 + 0.296772790257 + 0.302205095562 + 0.928446954142
 + 0.033615064623 + 0.038778684994 + 0.743281078457 + 0.931343341817 + 0.995992351352 + 0.803282407390
 + 0.714717982763 + 0.002658373156 + 0.366005349525 + 0.569351286490 + 0.515456813437 + 0.193641742784
 + 0.188781686796 + 0.622488518613 + 0.632796984155 + 0.343964602031 + 0.494069912343 + 0.891150139880
 + 0.526788287274 + 0.066698500327 + 0.236622057166 + 0.249176977739 + 0.881250574063 + 0.940333075706
 + 0.936703186575 + 0.400023784940 + 0.875090761246 + 0.485734931256 + 0.281568612107 + 0.493793875212
 + 0.021540268393 + 0.576960812516 + 0.330968114316 + 0.814755318215 + 0.964632238890 + 0.252849647521
 + 0.328316150100 + 0.831418052792 + 0.474425361099 + 0.877461270445 + 0.720632491736 + 0.719074649194
 + 0.698827578293 + 0.378885181918 + 0.661859236288 + 0.169773462717

119 solutions found in 4.039 seconds, sampling 8.21707e-118% of available space.

请注意,总和中只有46个数字是算法预先随机选择的其他34个。

答案 2 :(得分:0)

它没有我想象的那么好,但我花了太多时间来分享它

我的想法是你可以通过将大小为10的子集分成2来将问题从O(n ^ 10)减少到O(n ^ 5)。

首先,计算大小为5的所有子集,然后按它们的和模1对它们进行排序(这样总和在0到1之间)

然后答案包括添加两个大小为5的子集,以便:

  • 这些子集不相交
  • 总和为<e>1-e and <1+e>2-ee10^-12

如果你巧妙地遍历大小为5的子集(实际上,这是将O(n ^ 10)带到O(n ^ 5)的部分),这三个检查中的每一个都非常便宜

是的,这个解决方案是O(n ^ 5)。问题是我的解决方案:

  • 是Python,它不是最快的语言
  • 仍然需要计算大小为5的所有组合,并且“在600中选择5”是不可修复的(637262850120组合)

编辑:我只是抓取大小为600的大型列表中大小为40的随机子集并测试所有组合。如果它不起作用,我会抓取一个新的随机子集并再次执行。它可以通过多线程来改进

20分钟后step==44的输出结果为:

l = [0.06225774829854913, 0.21267040355189515, 0.21954445729288707, 0.21954445729288707, 0.24621125123532117, 0.24621125123532117, 0.36931687685298087, 0.4017542509913792, 0.41421356237309515, 0.41640786499873883, 0.6619037896906015]
>>> sum(l) + 0.529964086141668
3.999999999955324

注意跳过前43个步骤的hack,如果你想进行真正的计算则注释

from math import pow
from itertools import combinations
import itertools
import random
import time

# Constants
random.seed(1)
listLength = 40
halfsize = 5
halfsize2 = 6
x = 0.529964086141668
epsilon = pow(10,-10)

# Define your list here
#myList = [random.random() for i in range(listLength)]
items = [0.9705627484771391, 0.2788205960997061, 0.620499351813308, 0.0, 0.4222051018559565, 0.892443989449804, 0.41640786499873883, 0.0, 0.6491106406735181, 0.36931687685298087, 0.16552506059643868, 0.04159457879229578, 0.0, 0.04159457879229578, 0.16552506059643868, 0.36931687685298087, 0.6491106406735181, 0.0, 0.41640786499873883, 0.892443989449804, 0.4222051018559565, 0.0, 0.620499351813308, 0.2788205960997061, 0.9705627484771391, 0.2788205960997061, 0.556349186104045, 0.866068747318506, 0.21267040355189515, 0.6014705087354439, 0.038404810405298306, 0.5299640861416677, 0.08304597359457233, 0.7046999107196257, 0.4017542509913792, 0.18033988749894903, 0.045361017187261155, 0.0, 0.045361017187261155, 0.18033988749894903, 0.4017542509913792, 0.7046999107196257, 0.08304597359457233, 0.5299640861416677, 0.038404810405298306, 0.6014705087354439, 0.21267040355189515, 0.866068747318506, 0.556349186104045, 0.2788205960997061, 0.620499351813308, 0.866068747318506, 0.142135623730951, 0.45362404707370985, 0.8062484748656971, 0.20655561573370207, 0.6619037896906015, 0.18033988749894903, 0.7703296142690075, 0.4403065089105507, 0.19803902718556898, 0.049875621120889946, 0.0, 0.049875621120889946, 0.19803902718556898, 0.4403065089105507, 0.7703296142690075, 0.18033988749894903, 0.6619037896906015, 0.20655561573370207, 0.8062484748656971, 0.45362404707370985, 0.142135623730951, 0.866068747318506, 0.620499351813308, 0.0, 0.21267040355189515, 0.45362404707370985, 0.7279220613578552, 0.04159457879229578, 0.4017542509913792, 0.8166538263919687, 0.2956301409870008, 0.8488578017961039, 0.4868329805051381, 0.21954445729288707, 0.0553851381374173, 0.0, 0.0553851381374173, 0.21954445729288707, 0.4868329805051381, 0.8488578017961039, 0.2956301409870008, 0.8166538263919687, 0.4017542509913792, 0.04159457879229578, 0.7279220613578552, 0.45362404707370985, 0.21267040355189515, 0.0, 0.4222051018559565, 0.6014705087354439, 0.8062484748656971, 0.04159457879229578, 0.31370849898476116, 0.63014581273465, 0.0, 0.43398113205660316, 0.9442719099991592, 0.5440037453175304, 0.24621125123532117, 0.06225774829854913, 0.0, 0.06225774829854913, 0.24621125123532117, 0.5440037453175304, 0.9442719099991592, 0.43398113205660316, 0.0, 0.63014581273465, 0.31370849898476116, 0.04159457879229578, 0.8062484748656971, 0.6014705087354439, 0.4222051018559565, 0.892443989449804, 0.038404810405298306, 0.20655561573370207, 0.4017542509913792, 0.63014581273465, 0.8994949366116654, 0.21954445729288707, 0.6023252670426267, 0.06225774829854913, 0.6157731058639087, 0.28010988928051805, 0.0710678118654755, 0.0, 0.0710678118654755, 0.28010988928051805, 0.6157731058639087, 0.06225774829854913, 0.6023252670426267, 0.21954445729288707, 0.8994949366116654, 0.63014581273465, 0.4017542509913792, 0.20655561573370207, 0.038404810405298306, 0.892443989449804, 0.41640786499873883, 0.5299640861416677, 0.6619037896906015, 0.8166538263919687, 0.0, 0.21954445729288707, 0.48528137423856954, 0.810249675906654, 0.21110255092797825, 0.7082039324993694, 0.32455532033675905, 0.08276253029821934, 0.0, 0.08276253029821934, 0.32455532033675905, 0.7082039324993694, 0.21110255092797825, 0.810249675906654, 0.48528137423856954, 0.21954445729288707, 0.0, 0.8166538263919687, 0.6619037896906015, 0.5299640861416677, 0.41640786499873883, 0.0, 0.08304597359457233, 0.18033988749894903, 0.2956301409870008, 0.43398113205660316, 0.6023252670426267, 0.810249675906654, 0.0710678118654755, 0.40312423743284853, 0.8309518948453007, 0.38516480713450374, 0.09901951359278449, 0.0, 0.09901951359278449, 0.38516480713450374, 0.8309518948453007, 0.40312423743284853, 0.0710678118654755, 0.810249675906654, 0.6023252670426267, 0.43398113205660316, 0.2956301409870008, 0.18033988749894903, 0.08304597359457233, 0.0, 0.6491106406735181, 0.7046999107196257, 0.7703296142690075, 0.8488578017961039, 0.9442719099991592, 0.06225774829854913, 0.21110255092797825, 0.40312423743284853, 0.6568542494923806, 0.0, 0.4721359549995796, 0.12310562561766059, 0.0, 0.12310562561766059, 0.4721359549995796, 0.0, 0.6568542494923806, 0.40312423743284853, 0.21110255092797825, 0.06225774829854913, 0.9442719099991592, 0.8488578017961039, 0.7703296142690075, 0.7046999107196257, 0.6491106406735181, 0.36931687685298087, 0.4017542509913792, 0.4403065089105507, 0.4868329805051381, 0.5440037453175304, 0.6157731058639087, 0.7082039324993694, 0.8309518948453007, 0.0, 0.24264068711928477, 0.6055512754639891, 0.16227766016837952, 0.0, 0.16227766016837952, 0.6055512754639891, 0.24264068711928477, 0.0, 0.8309518948453007, 0.7082039324993694, 0.6157731058639087, 0.5440037453175304, 0.4868329805051381, 0.4403065089105507, 0.4017542509913792, 0.36931687685298087, 0.16552506059643868, 0.18033988749894903, 0.19803902718556898, 0.21954445729288707, 0.24621125123532117, 0.28010988928051805, 0.32455532033675905, 0.38516480713450374, 0.4721359549995796, 0.6055512754639891, 0.8284271247461903, 0.2360679774997898, 0.0, 0.2360679774997898, 0.8284271247461903, 0.6055512754639891, 0.4721359549995796, 0.38516480713450374, 0.32455532033675905, 0.28010988928051805, 0.24621125123532117, 0.21954445729288707, 0.19803902718556898, 0.18033988749894903, 0.16552506059643868, 0.04159457879229578, 0.045361017187261155, 0.049875621120889946, 0.0553851381374173, 0.06225774829854913, 0.0710678118654755, 0.08276253029821934, 0.09901951359278449, 0.12310562561766059, 0.16227766016837952, 0.2360679774997898, 0.41421356237309515, 0.0, 0.41421356237309515, 0.2360679774997898, 0.16227766016837952, 0.12310562561766059, 0.09901951359278449, 0.08276253029821934, 0.0710678118654755, 0.06225774829854913, 0.0553851381374173, 0.049875621120889946, 0.045361017187261155, 0.04159457879229578, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.04159457879229578, 0.045361017187261155, 0.049875621120889946, 0.0553851381374173, 0.06225774829854913, 0.0710678118654755, 0.08276253029821934, 0.09901951359278449, 0.12310562561766059, 0.16227766016837952, 0.2360679774997898, 0.41421356237309515, 0.0, 0.41421356237309515, 0.2360679774997898, 0.16227766016837952, 0.12310562561766059, 0.09901951359278449, 0.08276253029821934, 0.0710678118654755, 0.06225774829854913, 0.0553851381374173, 0.049875621120889946, 0.045361017187261155, 0.04159457879229578, 0.16552506059643868, 0.18033988749894903, 0.19803902718556898, 0.21954445729288707, 0.24621125123532117, 0.28010988928051805, 0.32455532033675905, 0.38516480713450374, 0.4721359549995796, 0.6055512754639891, 0.8284271247461903, 0.2360679774997898, 0.0, 0.2360679774997898, 0.8284271247461903, 0.6055512754639891, 0.4721359549995796, 0.38516480713450374, 0.32455532033675905, 0.28010988928051805, 0.24621125123532117, 0.21954445729288707, 0.19803902718556898, 0.18033988749894903, 0.16552506059643868, 0.36931687685298087, 0.4017542509913792, 0.4403065089105507, 0.4868329805051381, 0.5440037453175304, 0.6157731058639087, 0.7082039324993694, 0.8309518948453007, 0.0, 0.24264068711928477, 0.6055512754639891, 0.16227766016837952, 0.0, 0.16227766016837952, 0.6055512754639891, 0.24264068711928477, 0.0, 0.8309518948453007, 0.7082039324993694, 0.6157731058639087, 0.5440037453175304, 0.4868329805051381, 0.4403065089105507, 0.4017542509913792, 0.36931687685298087, 0.6491106406735181, 0.7046999107196257, 0.7703296142690075, 0.8488578017961039, 0.9442719099991592, 0.06225774829854913, 0.21110255092797825, 0.40312423743284853, 0.6568542494923806, 0.0, 0.4721359549995796, 0.12310562561766059, 0.0, 0.12310562561766059, 0.4721359549995796, 0.0, 0.6568542494923806, 0.40312423743284853, 0.21110255092797825, 0.06225774829854913, 0.9442719099991592, 0.8488578017961039, 0.7703296142690075, 0.7046999107196257, 0.6491106406735181, 0.0, 0.08304597359457233, 0.18033988749894903, 0.2956301409870008, 0.43398113205660316, 0.6023252670426267, 0.810249675906654, 0.0710678118654755, 0.40312423743284853, 0.8309518948453007, 0.38516480713450374, 0.09901951359278449, 0.0, 0.09901951359278449, 0.38516480713450374, 0.8309518948453007, 0.40312423743284853, 0.0710678118654755, 0.810249675906654, 0.6023252670426267, 0.43398113205660316, 0.2956301409870008, 0.18033988749894903, 0.08304597359457233, 0.0, 0.41640786499873883, 0.5299640861416677, 0.6619037896906015, 0.8166538263919687, 0.0, 0.21954445729288707, 0.48528137423856954, 0.810249675906654, 0.21110255092797825, 0.7082039324993694, 0.32455532033675905, 0.08276253029821934, 0.0, 0.08276253029821934, 0.32455532033675905, 0.7082039324993694, 0.21110255092797825, 0.810249675906654, 0.48528137423856954, 0.21954445729288707, 0.0, 0.8166538263919687, 0.6619037896906015, 0.41640786499873883, 0.892443989449804, 0.038404810405298306, 0.20655561573370207, 0.4017542509913792, 0.63014581273465, 0.8994949366116654, 0.21954445729288707, 0.6023252670426267, 0.06225774829854913, 0.6157731058639087, 0.28010988928051805, 0.0710678118654755, 0.0, 0.0710678118654755, 0.28010988928051805, 0.6157731058639087, 0.06225774829854913, 0.6023252670426267, 0.21954445729288707, 0.8994949366116654, 0.63014581273465, 0.4017542509913792, 0.20655561573370207, 0.038404810405298306, 0.892443989449804, 0.4222051018559565, 0.6014705087354439, 0.8062484748656971, 0.04159457879229578, 0.31370849898476116, 0.63014581273465, 0.0, 0.43398113205660316, 0.9442719099991592, 0.5440037453175304, 0.24621125123532117, 0.06225774829854913, 0.0, 0.06225774829854913, 0.24621125123532117, 0.5440037453175304, 0.9442719099991592, 0.43398113205660316, 0.0, 0.63014581273465, 0.31370849898476116, 0.04159457879229578, 0.8062484748656971, 0.6014705087354439, 0.4222051018559565, 0.0, 0.21267040355189515, 0.45362404707370985, 0.7279220613578552, 0.04159457879229578, 0.4017542509913792, 0.8166538263919687, 0.2956301409870008, 0.8488578017961039, 0.4868329805051381, 0.21954445729288707, 0.0553851381374173, 0.0, 0.0553851381374173, 0.21954445729288707, 0.4868329805051381, 0.8488578017961039, 0.2956301409870008, 0.8166538263919687, 0.4017542509913792, 0.04159457879229578, 0.7279220613578552, 0.45362404707370985, 0.21267040355189515, 0.0, 0.620499351813308, 0.866068747318506, 0.142135623730951, 0.45362404707370985, 0.8062484748656971, 0.20655561573370207, 0.6619037896906015, 0.18033988749894903, 0.7703296142690075, 0.4403065089105507, 0.19803902718556898, 0.049875621120889946, 0.0, 0.049875621120889946, 0.19803902718556898, 0.4403065089105507, 0.7703296142690075, 0.18033988749894903, 0.6619037896906015, 0.20655561573370207, 0.8062484748656971, 0.45362404707370985, 0.142135623730951, 0.866068747318506, 0.620499351813308, 0.2788205960997061, 0.556349186104045, 0.866068747318506, 0.21267040355189515, 0.6014705087354439, 0.038404810405298306, 0.5299640861416677, 0.08304597359457233, 0.7046999107196257, 0.4017542509913792, 0.18033988749894903, 0.045361017187261155, 0.0, 0.045361017187261155, 0.18033988749894903, 0.4017542509913792, 0.7046999107196257, 0.08304597359457233, 0.5299640861416677, 0.038404810405298306, 0.6014705087354439, 0.21267040355189515, 0.866068747318506, 0.556349186104045, 0.2788205960997061, 0.9705627484771391, 0.2788205960997061, 0.620499351813308, 0.0, 0.4222051018559565, 0.892443989449804, 0.41640786499873883, 0.0, 0.6491106406735181, 0.36931687685298087, 0.16552506059643868, 0.04159457879229578, 0.0, 0.04159457879229578, 0.16552506059643868, 0.36931687685298087, 0.6491106406735181, 0.0, 0.41640786499873883, 0.892443989449804, 0.4222051018559565, 0.0, 0.620499351813308, 0.2788205960997061, 0.9705627484771391]
items = sorted(items)
#print(len(items))
#print(items)

itemSet = sorted(list(set(items)))
#print(len(itemSet))
#print(itemSet)
#print(myList)

#Utility functions
#s is a set of indices
def mySum(s):
  return (sum([myList[i] for i in s]))%1

def mySum2(s):
  return (sum([myList[i] for i in s]) + x)%1


start = time.time()


for step in range(1, 1000000):

    myList = random.sample(items,  listLength)
    # HACK
    if(step<44):
        continue

    print("Step %s"%(step))
    myList = sorted(myList)

    listHalfIndices = [i for i in combinations(range(len(myList)),halfsize)]
    listHalfIndices1 = sorted(listHalfIndices, key = mySum)
    #print(listHalfIndices)
    #print([mySum(s) for s in listHalfIndices])

    listHalfIndices2 = [i for i in combinations(range(len(myList)),halfsize2)]
    listHalfIndices2 = sorted(listHalfIndices2, key = mySum2)
    #print(listHalfIndices2)
    #print([mySum2(s) for s in listHalfIndices2])
    """
    # SKIP THIS as I heuristically noted that it was pretty useless
    # First answer if the sum of the first and second list is smaller than epsilon
    print("ANSWER TYPE 1")
    #print([mySum(s) for s in listHalfIndices1[0:10]])
    #print([mySum2(s) for s in listHalfIndices2[0:10]])


    listLowIndices1 = [s for s in listHalfIndices1 if mySum(s) <= epsilon]
    #print(listLowIndices1)
    #print([mySum(s) for s in listLowIndices1])

    listLowIndices2 = [s for s in listHalfIndices2 if mySum2(s) <= epsilon]
    #print(listLowIndices2)
    #print([mySum2(s) for s in listLowIndices2])

    combinationOfIndices1 = [list(set(sum(i, ()))) for i in itertools.chain(itertools.product(listLowIndices1, listLowIndices2))]
    #print(combinationOfIndices1)
    #print([mySum2(s) for s in combinationOfIndices1])

    answer1 = [i for i in combinationOfIndices1 if len(i) == (2*halfsize) and mySum2(i)<=epsilon]
    if(len(answer1) > 0):
        print (answer1)
        print([mySum2(s) for s in answer1])
        break

    # Second answer if the sum of the first and second list is larger than 2-epsilon
    print("ANSWER TYPE 2")
    #print([mySum(s) for s in listHalfIndices1[-10:-1]])
    #print([mySum2(s) for s in listHalfIndices2[-10:-1]])

    listHighIndices1 = [s for s in listHalfIndices1 if mySum(s) >= 1-epsilon]
    #print(listHighIndices1)

    listHighIndices2 = [s for s in listHalfIndices2 if mySum2(s) >= 1-epsilon]
    #print(listHighIndices2)

    combinationOfIndices2 = [list(set(sum(i, ()))) for i in itertools.chain(itertools.product(listHighIndices1, listHighIndices2))]
    #print(combinationOfIndices2)
    #print([mySum2(s) for s in combinationOfIndices2])

    answer2 = [i for i in combinationOfIndices2 if len(i) == (2*halfsize) and mySum2(i)>=1-epsilon]
    if(len(answer2) > 0):
        print (answer2)
        print([mySum2(s) for s in answer2])
        break
    """
    # Third answer if the sum of the first and second list is between 1-epsilon and 1+epsilon
    #print("ANSWER TYPE 3")
    i = 0
    j = len(listHalfIndices2) - 1
    answer3 = None
    print("Answer of type 3 will explore at most %s combinations "%(2*len(listHalfIndices2)))
    for k in range(2*len(listHalfIndices2)): 
        if(k%1000000 == 0 and k>0):
                print(k)
        setOfIndices = list(set(listHalfIndices1[i] + listHalfIndices2[j]))
        #print(setOfIndices)

        currentSum = mySum(listHalfIndices1[i]) + mySum2(listHalfIndices2[j])
        #print(currentSum)
        if(currentSum < 1-epsilon):
            i = i+1
            if(i>=len(listHalfIndices1)):
                break
            #print("i++")
        else:
            j = j-1
            if(j<0):
              break
            #print("j--")
        if(len(setOfIndices) < (halfsize+halfsize2)):
            #print("skipping")
            continue

        if(currentSum >= 1-epsilon and currentSum <= 1+epsilon):
            print("Found smart combination with sum : s=%s"%(currentSum))
            print(sorted([myList[i] for i in setOfIndices]))
            answer3 = setOfIndices
            break

    if answer3 is not None:
        break

end = time.time()
print("Time elapsed %s"%(end-start))