Iterating over integer arrays with fixed sum in Julia - algorithm

I am looking for an algorithm to iterate over all arrays of length n whose entries are integers between 0 and d and whose sum is k*d. It would be even better if there is a way to do this with built-in Julia functions and iterators. The algorithm should be non-recursive and memory efficient as I am hoping to use this for reasonable values of n.
For small values of n, d, and k, I've written down all such arrays in lexicographical ordering, but I haven't been able to come up with code for iterating through all such arrays.

I think this should work but it requires Combinatorics.jl and ResumableFunctions.jl
using Combinatorics, ResumableFunctions
#resumable function gen_all(n, k, d)
for x in partitions(k*d + n, n)
x = x .- 1
if all(x .<= d)
ys = Set(permutations(x))
for y in ys
#yield y
end
end
end
end
for ga in gen_all(5, 2, 2)
println(ga)
end
gives
[2, 0, 0, 2, 0]
[2, 0, 0, 0, 2]
[0, 0, 2, 2, 0]
[0, 2, 2, 0, 0]
[2, 0, 2, 0, 0]
[0, 2, 0, 2, 0]
[2, 2, 0, 0, 0]
[0, 0, 0, 2, 2]
[0, 0, 2, 0, 2]
[0, 2, 0, 0, 2]
[0, 2, 0, 1, 1]
[0, 1, 1, 0, 2]
[0, 1, 2, 0, 1]
[0, 1, 1, 2, 0]
[2, 1, 1, 0, 0]
[2, 1, 0, 0, 1]
[0, 0, 1, 1, 2]
[1, 2, 1, 0, 0]
[1, 2, 0, 0, 1]
[0, 1, 2, 1, 0]
[0, 1, 0, 1, 2]
[1, 0, 0, 1, 2]
[0, 2, 1, 1, 0]
[2, 0, 0, 1, 1]
[1, 0, 2, 0, 1]
[1, 2, 0, 1, 0]
[0, 1, 0, 2, 1]
[2, 0, 1, 0, 1]
[0, 2, 1, 0, 1]
[1, 0, 1, 2, 0]
[0, 0, 1, 2, 1]
[1, 0, 0, 2, 1]
[2, 1, 0, 1, 0]
[1, 1, 0, 0, 2]
[1, 0, 2, 1, 0]
[1, 0, 1, 0, 2]
[1, 1, 0, 2, 0]
[0, 0, 2, 1, 1]
[2, 0, 1, 1, 0]
[1, 1, 2, 0, 0]
[1, 1, 1, 0, 1]
[1, 1, 0, 1, 1]
[1, 0, 1, 1, 1]
[1, 1, 1, 1, 0]
[0, 1, 1, 1, 1]

Related

Incorrect output for covering all graph edges

I need to cover all the edges of a graph with the minimum number of paths. I read that the Eulerian path method is needed here. I tried to reproduce it, but it doesn't work correctly.
def split_graph(graph, n):
stack = []
path = []
some_list = []
for i in range(n):
some_list .append(sum(graph[i]))
cur = 0
while (len(stack) > 0 or sum(graph[cur]) != 0):
if (sum(graph[cur]) == 0):
path.append(cur+1)
cur = stack[-1]
del stack[-1]
else:
for i in range(n):
if (graph[cur][i] == 1):
stack.append(cur)
graph[cur][i] = 0
graph[i][cur] = 0
cur = i
print(path)
if __name__ == '__main__':
graph = [[0, 1, 0, 1, 0, 0],
[1, 0, 1, 1, 0, 0],
[0, 1, 0, 0, 0, 0],
[1, 1, 0, 0, 0, 1],
[0, 0, 0, 0, 0, 1],
[0, 0, 0, 1, 1, 0]]
n = len(graph)
split_graph(graph, n)
Required output (There may be several options, this is one of them):
[6, 7, 4, 1, 2, 3]
[2,4]
[7, 5]
My output:
[3, 5, 6, 1, 4, 2]
How can this be fixed?

Select/4 on a list of lists

On prolog i'm trying to solve the following exercise for connect 4
?- generate_move(2,[[1, 0, 1, 0],[0, 1, 1, 0],[0, 0, 1, 1],[1, 0, 0, 1]],BP).
BP = [[1, 2, 1, 0], [0, 1, 1, 0], [0, 0, 1, 1], [1, 0, 0, 1]] ;
BP = [[1, 0, 1, 2], [0, 1, 1, 0], [0, 0, 1, 1], [1, 0, 0, 1]] ;
BP = [[1, 0, 1, 0], [2, 1, 1, 0], [0, 0, 1, 1], [1, 0, 0, 1]] ;
BP = [[1, 0, 1, 0], [0, 1, 1, 2], [0, 0, 1, 1], [1, 0, 0, 1]] ;
BP = [[1, 0, 1, 0], [0, 1, 1, 0], [2, 0, 1, 1], [1, 0, 0, 1]] ;
BP = [[1, 0, 1, 0], [0, 1, 1, 0], [0, 2, 1, 1], [1, 0, 0, 1]] ;
Where the rule is that is returns every possible move left for a player provided (in this case 2)
I was thinking of using select/4 to find every result possible for each list in the list of lists, then join it. But i'm failing to "condense" results and my second iteration of findall fails:
generate_move(_,[],[]).
generate_move(Player,[H|T],[FinalLine|FinalBoard]) :-
findall(X0,(select(0, H, Player, X0)),FinalLine),
join_results(FinalLine,T,FinalBoard),
generate_move(Player,T,FinalBoard).
join_results([],[],[]).
join_results([H|T],LeftOvers,FinalBoard) :-
append([H],LeftOvers,FinalBoard),
join_results(T,LeftOvers,FinalBoard).
Any tips? My join_results returns false at the second call and so does my findall.

Convert string to list of integers

This is the input string: 1,1,1,1,1,1,1,1,1,1,-9\n1,1,1,1,1,1,1,1,1,1,1\n2,1,2,1,2,1,2,1,2,1,2\n1,0,1,0,1,0\n3,1,2,-2,1,-2
I need to convert this string to lists of integers, so the output should be: [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -9], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2], [1, 0, 1, 0, 1, 0], [3, 1, 2, -2, 1, -2]]
My solution is:
text.split("\n").map do |row|
row.split(",").map(&:to_i)
end
Is there a better, more efficient way?
Refactored as a One-Liner
I often find that when people say "more efficient" or "more elegant" they just mean shorter or more compact. There's nothing wrong with your code, and unless you're performing actions on tens of thousands of arrays I doubt efficiency is anything other than a premature optimization. That said, you can write a one-liner for this if you want.
For example, using Ruby 2.7.1 and str as your input string:
str.split.map { _1.split(?,).map &:to_i }
#=> [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -9], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2], [1, 0, 1, 0, 1, 0], [3, 1, 2, -2, 1, -2]]
If you pretty-print the result with Kernel#pp, you'll get the results you expect.
[[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -9],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2],
[1, 0, 1, 0, 1, 0],
[3, 1, 2, -2, 1, -2]]
This isn't better than your existing code in any meaningful way, and I won't even claim it's faster. About all I can really say is that it's more compact and uses fewer lines, so please use your own judgment about its readability and utility value.

Searching through a matrix with Ruby

I have a matrix like this:
0 1 0 0 1 0
1 0 1 0 1 0
0 1 0 1 0 0
0 0 1 0 1 1
1 1 0 1 0 0
0 0 0 1 0 0
How can I define a matrix in Ruby, and then search through it?
I would like to write a program which searches through all rows and returns rows with the highest sum of "1".
Quick way to define a matrix in Ruby:
Array.new 6, Array.new(6, 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]
]
The above code initializes an array with 6 items and defaults their values to the 2nd argument which is another array with 6 items and default values of 0.
In other more imperative languages you would use nested loops:
matrix = []
for x in [0,1,2,3,4,5]
for y in [0,1,2,3,4,5]
matrix[x] ||= [] # create the row as an empty array
matrix[x] << y # push the y value to it
end
end
# matrix is now:
# => [
[0, 1, 2, 3, 4, 5],
[0, 1, 2, 3, 4, 5],
[0, 1, 2, 3, 4, 5],
[0, 1, 2, 3, 4, 5],
[0, 1, 2, 3, 4, 5],
[0, 1, 2, 3, 4, 5]
]
To search through the matrix and find the row with greatest sum:
greatest_sum_row_index = 0
greatest_sum = 0
matrix.each_with_index do |row, i|
# row.inject(:+) is a shortcut to adding each int in the array and returning the sum
sum = row.inject(:+)
if sum > greatest_sum
greatest_sum = sum
greatest_sum_row_index = i
end
end
# highest_row is now the index of the greatest sum row
matrix[greatest_sum_row_index] # returns the row with the greatest sum
If the array were large and memory requirements were a concern, you might do something like this:
def rows_with_most_ones(arr)
arr.each_with_index.with_object([-1, nil]) do |(row,i),best|
tot = row.count(1)
case tot<=>best.first
when 1 then best.replace([tot, [i]])
when 0 then best.last << i
end
end
end
arr = [[0, 1, 0, 0, 1, 0],
[1, 0, 1, 0, 1, 0],
[0, 1, 0, 1, 0, 0],
[0, 0, 1, 0, 1, 1],
[1, 1, 0, 1, 0, 0],
[0, 0, 0, 1, 0, 0]]
rows_with_most_ones(arr)
#=> [3, [1, 3, 4]]

Selecting values below a threshold and anchored at the left or right using Ruby NArray

Using NArray, is there some nifty way to create masks of arrays with values below e.g. 5, but only for runs of values anchored a the left or right side, E.g. this 1-D array:
[3, 4, 5, 7, 1, 7, 8]
would result in:
[1, 1, 0, 0, 0, 0, 0]
And this 2-D array:
[[2, 4, 5, 7, 1, 2, 3],
[3, 4, 5, 7, 1, 7, 8],
[8, 1, 1, 7, 1, 7, 1]]
would result in:
[[1, 1, 0, 0, 1, 1, 1],
[1, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1]]
require "narray"
def anchor_mask(mask)
idx = (mask.not).where
y = idx/mask.shape[0]
u = (y[0..-2].ne y[1..-1]).where
t = [0] + (u+1).to_a + [idx.size]
s = (0..u.size).map{|i| idx[t[i]]..idx[t[i+1]-1]}
mask[s] = 0
return mask
end
a = NArray[3, 4, 5, 7, 1, 7, 8]
p anchor_mask a.lt(5)
#=> NArray.byte(7):
# [ 1, 1, 0, 0, 0, 0, 0 ]
a = NArray[[2, 4, 5, 7, 1, 2, 3],
[3, 4, 5, 7, 1, 7, 8],
[8, 1, 1, 7, 1, 7, 1]]
p anchor_mask a.lt(5)
#=> NArray.byte(7,3):
# [ [ 1, 1, 0, 0, 1, 1, 1 ],
# [ 1, 1, 0, 0, 0, 0, 0 ],
# [ 0, 0, 0, 0, 0, 0, 1 ] ]

Resources