1. Murphy still winning
- Posted by rforno at tutopia.com
Apr 21, 2003
Pete and all:
I corrected the small bug you mentioned. I also found another bug and
corrected it. I made a slight improvement. But, using sequences such as the
one included in the listing, the program has to actually backtrack and so it
uses much time.
Murphy wins. I was not able to find more improvements and decided to let
things stand as they are now.
But it is worth considering that, for random numbers, or better, for data
that is not especially prepared to defeat the algorithm, it is really fast
even with huge quantities of numbers.
Regards.
The "omega" version:
--Group numbers into two groups such that their sums are as close as
possible
--R. M. Forno 2003/04/17
include sort.e
include get.e
include misc.e
include machine.e
integer Last, Lendata, Bestlast
atom Lowgoal, Highgoal, Val, Bestval, Totval, Limit, Bound
sequence Data, Index, Accum, Bestind
set_rand(1)
function Mcd2(integer d, integer r)
integer x
while r do
x = remainder(d, r)
d = r
r = x
end while
return d
end function
function Mcd(sequence s)
integer len, r
len = length(s)
r = s[len]
for i = len - 1 to 1 by -1 do
if r = 1 then
return r
end if
r = Mcd2(r, s[i])
end for
return r
end function
function readseq()
--Read a sequence of integers from the keyboard or from a redirected file
sequence s, r
s = {}
while 1 do
r = get(0)
if r[1] != GET_SUCCESS or not integer(r[2]) then
exit
end if
s &= r[2]
end while
return s
end function
procedure outputresult()
--Output resulting partitions
sequence aux
aux = repeat(0, Lendata)
for i = 1 to Lendata do
aux[i] = i
end for
for i = 1 to Bestlast do
aux[Bestind[i]] = 0
end for
printf(1, "Best solution values: %d %d\n", {Bestval, Totval - Bestval})
puts(1, "Partition 1:\n")
for i = 1 to Bestlast do
printf(1, "%d ", Data[Bestind[i]])
end for
puts(1, '\n')
puts(1, "Partition 2:\n")
for i = 1 to Lendata do
if aux[i] then
printf(1, "%d ", Data[aux[i]])
end if
end for
puts(1, '\n')
end procedure
function recurse()
integer ind, r, dat, i1
if Val > Highgoal then --Impossible to get better values
return 0
elsif Val < Lowgoal then --Try to go ahead
if Val >= Bound then
if Val > Bestval then --Test for better value
Bestval = Val --Update best value and corresponding indexes
Bestlast = Last
Bestind = Index
end if
end if
if Val > Limit then --Impossible to get better values
return 0
end if
ind = Index[Last] --Go to next data item
while 1 do
i1 = ind --Previous
ind += 1
if ind > Lendata then --End of data vector
return 0
end if
if Val + Accum[ind] <= Bestval then --Impossible to get better value
return 0
end if
dat = Data[ind]
if dat != Data[i1] or Index[Last] = i1 then --Skip repetition
Last += 1 --Try next index
Index[Last] = ind
Val += dat
r = recurse()
if r then --Best possible value found
return 1
end if
Val -= dat --Restore previous value
Last -= 1
end if
end while
else
Bestval = Val --Either Val = Lowgoal or Val = Highgoal
Bestlast = Last
Bestind = Index
return 1 --Indicate best possible value found
end if
end function
procedure optim()
integer r
Bestval = 0
Last = 1
Bestlast = 1
Index[1] = 1
Val = Data[1]
Limit = Highgoal - Data[Lendata]
Bound = Lowgoal - Data[1]
Bestind = Index
r = recurse()
if Bestval = 0 then
Bestval = Data[1]
end if
end procedure
function genrand(integer n, integer m)
sequence s
s = 14 * rand(repeat(n, m))
s[1] += 1
s[length(s)] += 1
return s
end function
procedure main()
atom t
integer a, b, mcd, r
a = 10000
b = 28
-- Data = readseq()
Data = genrand(a, b)
-- Data = {111, 9, 9, 9, 9, 9, 5, 1, 1, 1}
t = time()
Lendata = length(Data)
Index = repeat(0, Lendata)
if Lendata = 0 then
Bestlast = 0
Totval = 0
Bestval = 0
elsif Lendata = 1 then
Bestlast = 1
Bestind = {1}
Totval = Data[1]
Bestval = Totval
else
Data = reverse(sort(Data))
Accum = Data
for i = Lendata to 2 by -1 do
Accum[i - 1] += Accum[i]
end for
Totval = Accum[1]
Lowgoal = floor(Totval * 0.5)
Highgoal = - floor(- Totval * 0.5)
mcd = Mcd(Data)
Lowgoal -= remainder(Lowgoal, mcd)
r = remainder(Highgoal, mcd)
if r then
Highgoal += mcd - r
end if
if Totval - Highgoal < Lowgoal then
Lowgoal = Totval - Highgoal
end if
if Totval - Lowgoal > Highgoal then
Highgoal = Totval - Lowgoal
end if
printf(2, "Lowgoal = %d, Highgoal = %d\n", {Lowgoal, Highgoal})
optim()
end if
printf(2, "n = %d m = %d Sum1 = %d Sum2 = %d Time: %f\n",
{a, b, Bestval, Totval - Bestval, time() - t})
outputresult()
end procedure
main()
2. Re: Murphy still winning
Can you make this so that the number of groups is variable? For instance,
sometimes I need to put those values across 3 or 4 or more buckets... :)
----- Original Message -----
From: <rforno at tutopia.com>
To: "EUforum" <EUforum at topica.com>
Subject: Murphy still winning
>
> The "omega" version:
>
> --Group numbers into two groups such that their sums are as close as
> possible
> --R. M. Forno 2003/04/17
> include sort.e
> include get.e
> include misc.e
> include machine.e
>
> integer Last, Lendata, Bestlast
> atom Lowgoal, Highgoal, Val, Bestval, Totval, Limit, Bound
> sequence Data, Index, Accum, Bestind
>
> set_rand(1)
>
> function Mcd2(integer d, integer r)
> integer x
> while r do
> x = remainder(d, r)
> d = r
> r = x
> end while
> return d
> end function
>
> function Mcd(sequence s)
> integer len, r
> len = length(s)
> r = s[len]
> for i = len - 1 to 1 by -1 do
> if r = 1 then
> return r
> end if
> r = Mcd2(r, s[i])
> end for
> return r
> end function
>
> function readseq()
> --Read a sequence of integers from the keyboard or from a redirected file
> sequence s, r
> s = {}
> while 1 do
> r = get(0)
> if r[1] != GET_SUCCESS or not integer(r[2]) then
> exit
> end if
> s &= r[2]
> end while
> return s
> end function
>
> procedure outputresult()
> --Output resulting partitions
> sequence aux
> aux = repeat(0, Lendata)
> for i = 1 to Lendata do
> aux[i] = i
> end for
> for i = 1 to Bestlast do
> aux[Bestind[i]] = 0
> end for
> printf(1, "Best solution values: %d %d\n", {Bestval, Totval -
Bestval})
> puts(1, "Partition 1:\n")
> for i = 1 to Bestlast do
> printf(1, "%d ", Data[Bestind[i]])
> end for
> puts(1, '\n')
> puts(1, "Partition 2:\n")
> for i = 1 to Lendata do
> if aux[i] then
> printf(1, "%d ", Data[aux[i]])
> end if
> end for
> puts(1, '\n')
> end procedure
>
> function recurse()
> integer ind, r, dat, i1
> if Val > Highgoal then --Impossible to get better values
> return 0
> elsif Val < Lowgoal then --Try to go ahead
> if Val >= Bound then
> if Val > Bestval then --Test for better value
> Bestval = Val --Update best value and corresponding indexes
> Bestlast = Last
> Bestind = Index
> end if
> end if
> if Val > Limit then --Impossible to get better values
> return 0
> end if
> ind = Index[Last] --Go to next data item
> while 1 do
> i1 = ind --Previous
> ind += 1
> if ind > Lendata then --End of data vector
> return 0
> end if
> if Val + Accum[ind] <= Bestval then --Impossible to get better
value
> return 0
> end if
> dat = Data[ind]
> if dat != Data[i1] or Index[Last] = i1 then --Skip repetition
> Last += 1 --Try next index
> Index[Last] = ind
> Val += dat
> r = recurse()
> if r then --Best possible value found
> return 1
> end if
> Val -= dat --Restore previous value
> Last -= 1
> end if
> end while
> else
> Bestval = Val --Either Val = Lowgoal or Val = Highgoal
> Bestlast = Last
> Bestind = Index
> return 1 --Indicate best possible value found
> end if
> end function
>
> procedure optim()
> integer r
> Bestval = 0
> Last = 1
> Bestlast = 1
> Index[1] = 1
> Val = Data[1]
> Limit = Highgoal - Data[Lendata]
> Bound = Lowgoal - Data[1]
> Bestind = Index
> r = recurse()
> if Bestval = 0 then
> Bestval = Data[1]
> end if
> end procedure
>
> function genrand(integer n, integer m)
> sequence s
> s = 14 * rand(repeat(n, m))
> s[1] += 1
> s[length(s)] += 1
> return s
> end function
>
> procedure main()
> atom t
> integer a, b, mcd, r
> a = 10000
> b = 28
> -- Data = readseq()
> Data = genrand(a, b)
> -- Data = {111, 9, 9, 9, 9, 9, 5, 1, 1, 1}
> t = time()
> Lendata = length(Data)
> Index = repeat(0, Lendata)
> if Lendata = 0 then
> Bestlast = 0
> Totval = 0
> Bestval = 0
> elsif Lendata = 1 then
> Bestlast = 1
> Bestind = {1}
> Totval = Data[1]
> Bestval = Totval
> else
> Data = reverse(sort(Data))
> Accum = Data
> for i = Lendata to 2 by -1 do
> Accum[i - 1] += Accum[i]
> end for
> Totval = Accum[1]
> Lowgoal = floor(Totval * 0.5)
> Highgoal = - floor(- Totval * 0.5)
> mcd = Mcd(Data)
> Lowgoal -= remainder(Lowgoal, mcd)
> r = remainder(Highgoal, mcd)
> if r then
> Highgoal += mcd - r
> end if
> if Totval - Highgoal < Lowgoal then
> Lowgoal = Totval - Highgoal
> end if
> if Totval - Lowgoal > Highgoal then
> Highgoal = Totval - Lowgoal
> end if
> printf(2, "Lowgoal = %d, Highgoal = %d\n", {Lowgoal, Highgoal})
> optim()
> end if
> printf(2, "n = %d m = %d Sum1 = %d Sum2 = %d Time: %f\n",
> {a, b, Bestval, Totval - Bestval, time() - t})
> outputresult()
> end procedure
>
> main()
>
>
>
> TOPICA - Start your own email discussion group. FREE!
>
<snip>