Related
Given a number of digits n = 1, 2, 3, 4, 5, 6.
I wanted to generate a nested array S that will contain all possible 4 digit permutations of n.
since 6^4 = 1296, there will be 1296 possible permutations.
Example:
S = [[1,1,1,1],[1,1,1,2],[1,1,2,2]...[6,6,6,6]]
I started the nested loop with the first index with value of [1,1,1,1]
Then used a for in loop with range 0..1295 and tried to carry over the value of S[i] to S[i+1]
then increment the value of S[i+1][x], where x always starts at 3 then is decremented until it reaches 0 then it becomes 3 again. The problem with my procedure is when i try to increment the S[i+1][x], S[i] also increments its S[i][x].
In the code below S is instead called 'all_possible_combinations'
all_possible_combinations = Array.new(1296) {Array.new(4)}
all_possible_combinations[0] = [1, 1 ,1 ,1]
x = 3
for i in 0..1295
if i + 1 == 1296
break
else
all_possible_combinations[i+1] = all_possible_combinations[i]
all_possible_combinations[i+1][x] += 1
x -= 1
if x == 0
x = 3
end
end
end
[Attached image shows debugging process where Si][x] also gets incremented
You may compute that array as follows.
a = [1, 2, 3, 4, 5, 6]
b = a.repeated_permutation(4).to_a
#=> [[1, 1, 1, 1], [1, 1, 1, 2], [1, 1, 1, 3], [1, 1, 1, 4], [1, 1, 1, 5],
# [1, 1, 1, 6], [1, 1, 2, 1], [1, 1, 2, 2], [1, 1, 2, 3], [1, 1, 2, 4],
# ...
# [6, 6, 5, 3], [6, 6, 5, 4], [6, 6, 5, 5], [6, 6, 5, 6], [6, 6, 6, 1],
# [6, 6, 6, 2], [6, 6, 6, 3], [6, 6, 6, 4], [6, 6, 6, 5], [6, 6, 6, 6]]
b.size
#=> 1296
See Array#repeated_permutation
If the array a may contain duplicates and you wish to remove duplicate permutations you may wish to tack on Array#uniq.
a = [1, 1, 3, 1, 1, 6]
b = a.repeated_permutation(4).to_a.uniq
#=> [[1, 1, 1, 1], [1, 1, 1, 3], [1, 1, 1, 6], [1, 1, 3, 1],
# [1, 1, 3, 3], [1, 1, 3, 6], [1, 1, 6, 1], [1, 1, 6, 3],
# [1, 1, 6, 6], [1, 3, 1, 1], [1, 3, 1, 3], [1, 3, 1, 6],
# [1, 3, 3, 1], [1, 3, 3, 3], [1, 3, 3, 6], [1, 3, 6, 1],
# [1, 3, 6, 3], [1, 3, 6, 6], [1, 6, 1, 1], [1, 6, 1, 3],
# [1, 6, 1, 6], [1, 6, 3, 1], [1, 6, 3, 3], [1, 6, 3, 6],
# [1, 6, 6, 1], [1, 6, 6, 3], [1, 6, 6, 6], [3, 1, 1, 1],
# [3, 1, 1, 3], [3, 1, 1, 6], [3, 1, 3, 1], [3, 1, 3, 3],
# [3, 1, 3, 6], [3, 1, 6, 1], [3, 1, 6, 3], [3, 1, 6, 6],
# [3, 3, 1, 1], [3, 3, 1, 3], [3, 3, 1, 6], [3, 3, 3, 1],
# [3, 3, 3, 3], [3, 3, 3, 6], [3, 3, 6, 1], [3, 3, 6, 3],
# [3, 3, 6, 6], [3, 6, 1, 1], [3, 6, 1, 3], [3, 6, 1, 6],
# [3, 6, 3, 1], [3, 6, 3, 3], [3, 6, 3, 6], [3, 6, 6, 1],
# [3, 6, 6, 3], [3, 6, 6, 6], [6, 1, 1, 1], [6, 1, 1, 3],
# [6, 1, 1, 6], [6, 1, 3, 1], [6, 1, 3, 3], [6, 1, 3, 6],
# [6, 1, 6, 1], [6, 1, 6, 3], [6, 1, 6, 6], [6, 3, 1, 1],
# [6, 3, 1, 3], [6, 3, 1, 6], [6, 3, 3, 1], [6, 3, 3, 3],
# [6, 3, 3, 6], [6, 3, 6, 1], [6, 3, 6, 3], [6, 3, 6, 6],
# [6, 6, 1, 1], [6, 6, 1, 3], [6, 6, 1, 6], [6, 6, 3, 1],
# [6, 6, 3, 3], [6, 6, 3, 6], [6, 6, 6, 1], [6, 6, 6, 3],
# [6, 6, 6, 6]]
b.size
#=> 81
To create a sequence where each element is generated based on the previous one, there's Enumerator.produce, e.g.:
enum = Enumerator.produce([1, 1, 1, 1]) do |a, b, c, d|
d += 1 # ^^^^^^^^^^^^
# initial value
if d > 6
d = 1
c += 1
end
if c > 6
c = 1
b += 1
end
if b > 6
b = 1
a += 1
end
if a > 6
raise StopIteration # <- stops enumeration
end
[a, b, c, d] # <- return value = next value
end
I've kept the example intentionally simple, using an explicit variable for each of the four digits. You could of course also have an array and use a little loop to handle the increment / carry.
The above gives you:
enum.count #=> 1296
enum.first(3) #=> [[1, 1, 1, 1], [1, 1, 1, 2], [1, 1, 1, 3]]
enum.to_a.last(3) #=> [[6, 6, 6, 4], [6, 6, 6, 5], [6, 6, 6, 6]]
I have written the following program to find the permutation of all the elements in an array. The values are created properly but the problem occurs when I try to assign the generated sequence into a new array. The old values will get cleared and the new values are copied as per the array size
def find_perm(nums, answer, set)
if nums.empty?
p set
answer.push(set)
p answer.object_id
p answer
return true
end
for i in (0..nums.length - 1) do
new_nums = nums.clone
new_nums.delete_at(i)
set.push(nums[i])
find_perm(new_nums, answer, set)
set.pop
end
end
def permute(nums)
answer = []
set = []
element = find_perm(nums, answer, set)
return element
end
permute([1,2,3])
This are the observations that I have found out while debugging:
[1, 2, 3]
47167191669680
[[1, 2, 3]]
[1, 3, 2]
47167191669680
[[1, 3, 2], [1, 3, 2]]
[2, 1, 3]
47167191669680
[[2, 1, 3], [2, 1, 3], [2, 1, 3]]
[2, 3, 1]
47167191669680
[[2, 3, 1], [2, 3, 1], [2, 3, 1], [2, 3, 1]]
[3, 1, 2]
47167191669680
[[3, 1, 2], [3, 1, 2], [3, 1, 2], [3, 1, 2], [3, 1, 2]]
[3, 2, 1]
47167191669680
[[3, 2, 1], [3, 2, 1], [3, 2, 1], [3, 2, 1], [3, 2, 1], [3, 2, 1]]
The problem was each time I was pushing the same set array into the answer array so each element in the answer array will have same reference (same object_id).
Solution: Is to clone the set array during the each push to the answer array so that each element have different reference.
The solution:
def fact(n)
return 1 if n == 1
n*fact(n-1)
end
def find_perm(nums, answer, set)
if nums.empty?
answer.push(set.clone)
end
for i in (0..nums.length - 1) do
new_nums = nums.clone
new_nums.delete_at(i)
set.push(nums[i])
find_perm(new_nums, answer, set)
set.pop
return answer if fact(nums.count) == answer.count
end
end
def permute(nums)
answer = []
set = []
element = find_perm(nums, answer, set)
return element
end
p permute([1,2,3])
I believe the approach you are taking is similar to the following.
Suppose we wish to obtain the permutations of the elements of the array
arr = [1, 2, 3, 4]
Begin with the array
[4]
This array has only a single perumutation:
perms3 = [[4]]
(3 in perms3 denotes the index of 4 in arr.) Now obtain the permuations of
[3, 4]
We see that is
perms2 = [[3, 4], [4, 3]]
We simply take each element of perms3 ([4] is the only one) and create two permuations by inserting 3 before 4 and then 3 after 4;
Now suppose the array were
[2, 3, 4]
Then
perms1 = [[2, 3, 4], [3, 2, 4], [3, 4, 2], [2, 4, 3], [4, 2, 3], [4, 3, 2]]
We create three 3-element arrays from [3, 4], one by inserting 2 before 3, one by inserting 2 between 3 and 4 and one by inserting 2 after 4. Simlarly, three 3-element arrays are generated from [4, 3] in a simlar way. This generates six arrays. (Indeed, 3! #=> 6).
Lastly we generating the 4! #=> 24 permuations of [1, 2, 3, 4] by inserting 1 in four locations of each element of perm1, the first four derived from perms1[0] #=> [2, 3, 4]:
[[1, 2, 3, 4], [2, 1, 3, 4], [2, 3, 1, 4], [2, 3, 4, 1]]
We can do this in code as follows.
def my_permutations(arr)
perms = [[arr.last]]
(arr.size-2).downto(0) do |i|
x = arr[i]
perms = perms.flat_map do |perm|
(0..(perm.size)).map { |i| perm.dup.insert(i, x) }
end
end
perms
end
my_permutations(arr)
#=> [[1, 2, 3, 4], [2, 1, 3, 4], [2, 3, 1, 4], [2, 3, 4, 1], [1, 3, 2, 4],
# [3, 1, 2, 4], [3, 2, 1, 4], [3, 2, 4, 1], [1, 3, 4, 2], [3, 1, 4, 2],
# [3, 4, 1, 2], [3, 4, 2, 1], [1, 2, 4, 3], [2, 1, 4, 3], [2, 4, 1, 3],
# [2, 4, 3, 1], [1, 4, 2, 3], [4, 1, 2, 3], [4, 2, 1, 3], [4, 2, 3, 1],
# [1, 4, 3, 2], [4, 1, 3, 2], [4, 3, 1, 2], [4, 3, 2, 1]]
See Enumerable#flat_map and Array#insert. Note that we need to make a copy of the array perm before invoking insert.
We could of course have gone "forward" in arr (starting with [[1]]), rather than "backward", though the elements of the array of permutations would be ordered differently.
I'm trying to create a program that takes a given sum and a given range of allowed addends and outputs the unique configurations of those addends which add up to the sum.
The use case is determining the possible combinations of different-sized multi-member districts to divide the members of a legislature into.
In a trivial example, given 15 legislators, and districts of minimum 3 and maximum 5 seats per district, the possible combinations are:
[3, 3, 3, 3, 3]
[4, 4, 4, 3]
[5, 4, 3, 3]
[5, 5, 5]
My initial thought was to start with the largest group of minimum-sized districts possible in a nested array, and add more entries by copying and modifying the previous entry. I don't know how to implement that approach, but I'm also not sure if it's even the right approach to this problem and I'm looking for suggestions.
def multi_member_districts
reps = 19
min = 3
max = 6
quomin, modmin = reps.divmod(min)
quomax, modmax = reps.divmod(max)
groups = Array.new(1) {Array.new}
(quomin - 1).times do groups[0].push(min) end
groups[0].unshift(min + modmin)
# PSEUDOCODE
# copy groups[i], insert copy at groups[i+1]
# remove the smallest element of groups[i+1] and spread it out across the other
# numbers in groups[i+1] in all configurations in which no element exceeds max
# check that there are no duplicate configurations
# repeat
puts "\nThe possible groups of districts are as follows:"
groups.each_index do |i|
(min..max).each do |j|
unless groups[i].count(j) == 0
puts ">> #{groups[i].count(j)} #{j}-member districts"
end
end
puts
puts "o-o-o-o-o-o-o-o-o-o-o-o-o-o"
end
end
multi_member_districts
EDIT_1:
A less trivial example, 19 legislators, 3-6 seats per district --
[4, 3, 3, 3, 3, 3]
[4, 4, 4, 4, 3]
[5, 5, 5, 4]
[5, 4, 4, 3, 3]
[5, 5, 3, 3, 3]
[6, 5, 5, 3]
[6, 4, 3, 3, 3]
[6, 5, 4, 4]
[6, 6, 4, 3]
EDIT_2: Clarified my question, cut down the code, hopefully more suitable
Let's first compute the combinations where each combination corresponds to an array arr where arr[i] equals the number of legislators assigned to district i. If, for example, there are 15 legislators and there must be between 3 and 5 assigned to each district, [3,3,4,5] and [5,3,4,3] would be distinct combinations. We can solve that problem using recursion.
def doit(nbr, rng)
return nil if nbr < rng.begin
recurse(nbr, rng)
end
def recurse(nbr, rng)
(rng.begin..[rng.end, nbr].min).each_with_object([]) do |n,arr|
if n == nbr
arr << [n]
elsif nbr-n >= rng.begin
recurse(nbr-n, rng).each { |a| arr << a.unshift(n) }
end
end
end
doit(15, 3..5)
#=> [[3, 3, 3, 3, 3], [3, 3, 4, 5], [3, 3, 5, 4], [3, 4, 3, 5],
# [3, 4, 4, 4], [3, 4, 5, 3], [3, 5, 3, 4], [3, 5, 4, 3], [4, 3, 3, 5],
# [4, 3, 4, 4], [4, 3, 5, 3], [4, 4, 3, 4], [4, 4, 4, 3], [4, 5, 3, 3],
# [5, 3, 3, 4], [5, 3, 4, 3], [5, 4, 3, 3], [5, 5, 5]]
doit(19, 3..6)
#=> [[3, 3, 3, 3, 3, 4], [3, 3, 3, 3, 4, 3], [3, 3, 3, 4, 3, 3],
# [3, 3, 3, 4, 6], [3, 3, 3, 5, 5], [3, 3, 3, 6, 4], [3, 3, 4, 3, 3, 3],
# ...
# [6, 5, 3, 5], [6, 5, 4, 4], [6, 5, 5, 3], [6, 6, 3, 4], [6, 6, 4, 3]]
doit(19, 3..6).size
#=> 111
The question is not concerned, however, with allocations to specific districts. To obtain the combinations of interest we may therefore write the following.
require 'set'
def really_doit(nbr, rng)
doit(nbr, rng).map(&:tally).uniq.map do |h|
h.flat_map { |k,v| [k]*v }.sort.reverse
end
end
really_doit(15, 3..5)
#=> [[3, 3, 3, 3, 3], [5, 4, 3, 3], [4, 4, 4, 3], [5, 5, 5]]
really_doit(19, 3..6)
#=> [[4, 3, 3, 3, 3, 3], [6, 4, 3, 3, 3], [5, 5, 3, 3, 3],
# [5, 4, 4, 3, 3], [4, 4, 4, 4, 3], [6, 6, 4, 3], [6, 5, 5, 3],
# [6, 5, 4, 4], [5, 5, 5, 4]]
Enumerable#tally made its debut in Ruby v2.7. To support earlier versions replace map(&:tally) with map { |a| a.each_with_object(Hash.new(0)) { |n,h| h[n] += 1 }.
Note that doit(nbr, rng).map(&:tally).uniq in returns
[{3=>5}, {3=>2, 4=>1, 5=>1}, {3=>1, 4=>3}, {5=>3}]
in really_doit(15, 3..5) and
[{3=>5, 4=>1}, {3=>3, 4=>1, 6=>1}, {3=>3, 5=>2}, {3=>2, 4=>2, 5=>1},
{3=>1, 4=>4}, {3=>1, 4=>1, 6=>2}, {3=>1, 5=>2, 6=>1}, {4=>2, 5=>1, 6=>1},
{4=>1, 5=>3}]
in really_doit(19, 3..6).
We can improve on this by constructing sets of hashes (rather than arrays of arrays) in recurse:
require 'set'
def doit(nbr, rng)
return nil if nbr < rng.begin
recurse(nbr, rng).map { |h| h.flat_map { |k,v| [k]*v }.sort.reverse }
end
def recurse(nbr, rng)
(rng.begin..[rng.end, nbr].min).each_with_object(Set.new) do |n,st|
if n == nbr
st << { n=>1 }
elsif nbr-n >= rng.begin
recurse(nbr-n, rng).each { |h| st << h.merge(n=>h[n].to_i+1 ) }
end
end
end
doit(15, 3..5)
#=> [[3, 3, 3, 3, 3], [5, 4, 3, 3], [4, 4, 4, 3], [5, 5, 5]]
doit(19, 3..6)
#=> [[4, 3, 3, 3, 3, 3], [6, 4, 3, 3, 3], [5, 5, 3, 3, 3],
# [5, 4, 4, 3, 3], [4, 4, 4, 4, 3], [6, 6, 4, 3], [6, 5, 5, 3],
# [6, 5, 4, 4], [5, 5, 5, 4]]
Note that here recurse(nbr, rng) in doit returns:
#<Set: {{3=>5}, {5=>1, 4=>1, 3=>2}, {4=>3, 3=>1}, {5=>3}}>
When executing doit(19, 3..6) recurse(nbr, rng) in doit returns:
#<Set: {{4=>1, 3=>5}, {6=>1, 4=>1, 3=>3}, {5=>2, 3=>3},
# {5=>1, 4=>2, 3=>2}, {4=>4, 3=>1}, {6=>2, 4=>1, 3=>1},
# {6=>1, 5=>2, 3=>1}, {6=>1, 5=>1, 4=>2}, {5=>3, 4=>1}}>
I've had good read of the 'Sorting Functions' section of the Julia manual, and had a look at some of the similar questions that have already been asked on this board, but I don't think I've quite found the answer to my question. Apologies if I've missed something.
Essentially I have a vector of vectors, with the enclosed vectors containing integers. For the purposes of the example, each enclosed vector contains 3 integers, but it could be any number. I want to sort the enclosed vectors by the first element, then by the second element, then by the third element etc.
Let's start with the vector:
v = [[3, 6, 1], [2, 2, 6], [1, 5, 9], [2, 1, 8], [3, 7, 9],
[1, 1, 2], [2, 2, 2], [3, 6, 2], [1, 2, 5], [1, 5, 6],
[3, 7, 4], [2, 1, 4], [2, 2, 1], [3, 1, 2], [1, 2, 8]]
And continue with what I'm actually looking for:
v = [[1, 1, 2], [1, 2, 5], [1, 2, 8], [1, 5, 6], [1, 5, 9],
[2, 1, 4], [2, 1, 8], [2, 2, 1], [2, 2, 2], [2, 2, 6],
[3, 1, 2], [3, 6, 1], [3, 6, 2], [3, 7, 4], [3, 7, 9]]
So there should be no requirement for rocket science.
I can easily sort the vector by the first element of the enclosed vectors by one of two ways:
v = sort(v, lt = (x, y) -> isless(x[1], y[2]))
or:
v = sort(v, by = x -> x[1])
Both these methods produce the same answer:
v = [[1, 5, 9], [1, 1, 2], [1, 2, 5], [1, 5, 6], [1, 2, 8],
[2, 2, 6], [2, 1, 8], [2, 2, 2], [2, 1, 4], [2, 2, 1],
[3, 6, 1], [3, 7, 9], [3, 6, 2], [3, 7, 4], [3, 1, 2]]
So, as you can see, I have sorted by the first element of the enclosed vectors, but not by the subsequent elements.
So, to come back to the question in the title, is there a method of sorting by multiple elements using the sort() function?
I can actually get what I want using loops:
for i = 3:-1:1
v = sort(v, lt = (x, y) -> isless(x[i], y[i]))
end
or:
for i = 3:-1:1
v = sort(v, by = x -> x[i])
end
However, I don't want to re-invent the wheel, so if there's a way of doing it within the sort() function I'd love to learn about it.
You can use lexless function as lt keyword argument that does exactly what you want if I understood your question correctly:
julia> sort(v, lt=lexless)
15-element Array{Array{Int64,1},1}:
[1, 1, 2]
[1, 2, 5]
[1, 2, 8]
[1, 5, 6]
[1, 5, 9]
[2, 1, 4]
[2, 1, 8]
[2, 2, 1]
[2, 2, 2]
[2, 2, 6]
[3, 1, 2]
[3, 6, 1]
[3, 6, 2]
[3, 7, 4]
[3, 7, 9]
EDIT: I have just checked that this is a solution for Julia 0.6. In Julia 0.7 you can simply write:
julia> sort(v)
15-element Array{Array{Int64,1},1}:
[1, 1, 2]
[1, 2, 5]
[1, 2, 8]
[1, 5, 6]
[1, 5, 9]
[2, 1, 4]
[2, 1, 8]
[2, 2, 1]
[2, 2, 2]
[2, 2, 6]
[3, 1, 2]
[3, 6, 1]
[3, 6, 2]
[3, 7, 4]
[3, 7, 9]
I'm new to ruby and working on such a problem:
There are n numbered letters and n numbered envelopes. The letter x can't be
put into the envelope x.(OP only wants values where no value of x is at index x-1) What I want is to print out all the possible
cases.
The index of Array + 1 ---> the number of the envelop
The element of Array ---> the number of the letter
Input: n = 3.
Output: [2, 3, 1], [3, 1, 2]
Input: n = 4.
Output: [2, 1, 4, 3], [2, 3, 4, 1], [2, 4, 1, 3], [3, 1, 4, 2],
[3, 4, 1, 2], [3, 4, 2, 1], [4, 1, 2, 3], [4, 3, 1, 2],
[4, 3, 2, 1]
Here is my code:
$nums = []
def f( already, n, times )
if n > times
$nums << already.dup
return
else
1.upto(times) do |i|
next if ((already.include? i) || n == i)
already << i
f( already, n+1, times )
already.pop
end
end
end
I'm looking for more elegant solutions.
Make use of the permutation enumerator, rejecting all those where the value at index x-1 matches x:
def f(n, x)
(1..n).to_a.permutation.reject{|p| p[x-1] == x}
end
> f 3, 3
=> [[1, 3, 2], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
> f 4, 4
=> [[1, 2, 4, 3], [1, 3, 4, 2], [1, 4, 2, 3], [1, 4, 3, 2], [2, 1, 4, 3], [2, 3, 4, 1], [2, 4, 1, 3], [2, 4, 3, 1], [3, 1, 4, 2], [3, 2, 4, 1], [3, 4, 1, 2], [3, 4, 2, 1], [4, 1, 2, 3], [4, 1, 3, 2], [4, 2, 1, 3], [4, 2, 3, 1], [4, 3, 1, 2], [4, 3, 2, 1]]
UPDATE
Looking at your question again, it's unclear if you want to use a specific x, or just that the logic should hold true for any value of x. If the second guess is what you want, then use this instead:
def f(n)
(1..n).to_a.permutation.reject{|p| p.any?{|x| p[x-1] == x}}
end
> f 3
=> [[2, 3, 1], [3, 1, 2]]
> f 4
=> [[2, 1, 4, 3], [2, 3, 4, 1], [2, 4, 1, 3], [3, 1, 4, 2], [3, 4, 1, 2], [3, 4, 2, 1], [4, 1, 2, 3], [4, 3, 1, 2], [4, 3, 2, 1]]