Permutations in Ruby - unsure how to handle - ruby

I am trying to output an array of 10-element arrays containing permutations of 1 and 2, e.g.:
[[1,1,1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1,1,2],
[1,2,1,2,1,2,1,2,1,2],
...etc...
[2,2,2,2,2,2,2,2,2,2]]
I have done this with smaller arrays, but with (10):
a = [1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2]
a = a.permutation(10).to_a
print a.uniq
...this apparently is too big a calculation- after an hour running, it hadn't completed and the ruby process was sitting on 12GB of memory. Is there another way to come at this?

First, check size of that permutation
a.permutation(10).size
=> 670442572800
It's huge. What you can do instead is to use Array#repeated_permutations on smaller array. Check it here:
b = [1, 2] # Only unique elements
b.repeated_permutation(10) # Returns enumerable
b.repeated_permutation(10).to_a # Creates array with all of permutations
Those permutations are already unique (which u can check by printing it size with and without Array#uniq )

The approach you've gone down does indeed generate a lot of data - 20!/10! or about 600 billion arrays. This is clearly very wasteful when the output you are after only has 1024 arrays
what you are after is closer to the product method on array
[1,2].product(*([[1,2]]*9))
The product method produces all the possible combinations by picking one element from each of the receiver and its arguments. The use of splats and the * method on array is just to avoid writing [1,2] 9 times.

Edit: Don't use this... it's slower by a factor (10 times slower) then running: b= [1,2]; b.repeated_permutation(10).to_a
What you're looking for is actually a binary permutation array...
So taking that approach, we know we have 2**10 (== 1024) permutations, which translated to the numbers between 0 and 1023.
try this:
1024.times.with_object([]) {|i, array| array << ( ("%10b" % (i-1) ).unpack("U*").map {|v| (v == 49) ? 2 : 1} ) }
Or this (slightly faster):
(0..1023).each.with_object([]) {|i, array| array << ( (0..9).each.with_object([]) {|j, p| p << (i[j] +1)} ) }
You take the number of options - 1024. For each option (i) you assign a number (i-1) and extract the binary code that comprises that number.
The binary code is extracted, in my example, by converting it to a string 10 digits long using "%10b" % (i-1) and then unpacking that string to an array. I map that array, replacing the values I get from the string (white space == 32 && zero == 48) with the number 1 or (the number 1 == 49) with the number 2.
Voila.
There should be a better way to extract the binary representation of the numbers, but I couldn't think of one, as I'm running ob very little sleep.

Here's another way that is similar to the approach taken by #Myst, except I've used Fixnum#to_s to convert an integer to a string representation of its binary equivalent.
There are 2**n integers with n digits (including leading zeros), when each digit equals 1 or 2 (or equals 0 or 1). We therefore can 1-1 map each integer i between 0 and 2**n-1 to one of the integers containing only digits 1 and 2 by converting 0-bits to 1 and 1-bits to 2.
In Ruby, one way to convert 123 to binary is:
123.to_s(2).to_i #=> 1111011
As
2**9 #=> 512
(2**9-1).to_s(2) #=> "111111111"
(2**9-1).to_s(2).to_i #=> 111111111
we see that when comparing numbers between 0 and 511 (2**9-1), the binary representation of 123 would have two leading zeros. As we need those leading zeros to make the conversion to 1's and 2's, it is convenient to leave the binary representation of each numbers as a string, and pad the string with zeros:
str = 123.to_s(2).rjust(9,'0') #=> "001111011"
That allows us to write:
str.each_char.map { |c| c.eql?("0") ? 1 : 2 } }
#=> [1, 1, 2, 2, 2, 2, 1, 2, 2]
We can wrap this in a method:
def combos(n)
(2**n).times.map { |i| i.to_s(2)
.rjust(n,'0')
.each_char
.map { |c| c.eql?("0") ? 1 : 2 } }
end
combos(1)
#=> [[1], [2]]
combos(2)
#=> [[1, 1], [1, 2], [2, 1], [2, 2]]
combos(3)
#=> [[1, 1, 1], [1, 1, 2], [1, 2, 1], [1, 2, 2],
# [2, 1, 1], [2, 1, 2], [2, 2, 1], [2, 2, 2]]
combos(4)
#=> [[1, 1, 1, 1], [1, 1, 1, 2], [1, 1, 2, 1], [1, 1, 2, 2],
# [1, 2, 1, 1], [1, 2, 1, 2], [1, 2, 2, 1], [1, 2, 2, 2],
# [2, 1, 1, 1], [2, 1, 1, 2], [2, 1, 2, 1], [2, 1, 2, 2],
# [2, 2, 1, 1], [2, 2, 1, 2], [2, 2, 2, 1], [2, 2, 2, 2]]
combos(10).size
#=> 1024

Related

Why does my algorithm solution fail validation?

Here is a task i got:
Cards are laid out on the table in a row, each card has a natural number written on it. In one move, it is allowed to take a card either from the left or from the right end of the row. In total, you can make k moves. The final score is equal to the sum of the numbers on the selected cards. Determine what is the maximum score you can get at the end of the game.
Here`s my code:
def card_counter(arr, k):
if len(arr) == k:
return sum(arr)
rang = len(arr) // 2
left = arr[:rang]
right = list(reversed(arr[rang:]))
c = 0
for _ in range(k):
min_arr = left if sum(left) >= sum(
right) and len(left) > 0 else right
c += min_arr.pop(0)
return c
if __name__ == '__main__':
assert card_counter([1, 2, 3, 4, 5], 5) == 15
assert card_counter([0, 0, 0], 1) == 0
assert card_counter([150], 1) == 150
This code works on all variants that I have come up with, including extreme cases. But the system does not accept this option, automatic tests do not pass. Where can there be a mistake?
I cannot comment on the algorithm you implemented as you did not state it in non-algorithm terms and I am not familiar with the language you are using (which I am guessing is Python). I will present a simple solution written in Ruby, hoping that the description I give will make it understood by readers who do not know Ruby.
Suppose
deck = [1, 2, 5, 7, 1, 4, 6, 3]
nbr_moves = 4
ds = deck.size
#=> 8
After removing nbr_moves from the ends,
m = ds - nbr_moves
#=> 4
consecutive cards will remain. In Ruby we could write
best = (0..ds-1).each_cons(m).max_by { |arr| deck.values_at(*arr).sum }
#=> [3, 4, 5, 6]
to obtain the indices of deck, for which the sum of the associated values is maximum:
deck.values_at(*best).sum
#=> 18
where
deck.values_at(*best)
#=> [7, 1, 4, 6]
In view of the value of best ([3, 4, 5, 6]), we need to remove left = best.first #=> 3 elements from the left and nbr_moves - left #=> 1 element from the right. The order of the removals is not relevant.
Note that
enum = (0..ds-1).each_cons(m)
#=> #<Enumerator: 0..7:each_cons(4)>
returns an enumerator. We can convert this enumerator to an array to see the values it will generate.
enum.to_a
#=> [[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6], [4, 5, 6, 7]]
When, for example,
arr = [2, 3, 4, 5]
then
a = deck.values_at(*arr)
#=> [5, 7, 1, 4]
a.sum
#=> 17
Note that, having computed, for example,
t = [deck[i], deck[i+1],..., deck[j]].sum
the sum of
[deck[i+1], deck[i+2],..., deck[j+1]]
is seen to equal
t - deck[i] + deck[j+1]
This suggests a more efficient way to perform the calculations when m is large.

Ruby Second Smallest Number and index

Trying to use Ruby to find the second smallest number and the index from an input. I have the logic working from a hard coded array but can't seem to get the input from a user to work successfully. Thoughts?
print "Enter a list of numbers: "
nums = [gets]
#nums = [3,1,7,5]
lists = nums.sort
A = lists[1]
B = nums.find_index(A)
print "The second smallest number is: #{A} and the index is #{B}"
Suppose the user entered
str = " 2, -3 , 4, 1\n"
Then
arr = str.split(/ *, */).map(&:to_i)
#=> [2, -3, 4, 1]
The regular expression matches zero or more spaces followed by a comma followed by zero or more spaces.
The second smallest element, together with its index, can be obtained as follows.
n, i = arr.each_with_index.min(2).last
#=> [1, 3]
n #=> 1
i #=> 3
See Enumerable#min.
The steps are as follows.
enum = arr.each_with_index
#=> #<Enumerator: [2, -3, 4, 1]:each_with_index>
We can see the elements that will be generated by this enumerator by converting it to an array.
enum.to_a
#=> [[2, 0], [-3, 1], [4, 2], [1, 3]]
Continuing,
a = enum.min(2)
#=> [[-3, 1], [1, 3]]
min(2) returns the two smallest elements generated by enum. min compares each pair of elements with the method Array#<=>. (See especially the third paragraph of the doc.) For example,
[2, 0] <=> [-3, 1]
#=> 1
[4, 2] <=> [1, 3]
#=> 1
[1, 3] <=> [1, 4]
#=> -1
min would therefore order these pairs as follows.
[2, 0] > [-3, 1]
[4, 2] > [1, 3]
[1, 3] < [1, 4]
I've included the last example (though enum.to_a does not contain a second 1 at index 4) to illustrate that the second element of each two-element array serves as a tie-breaker.
As we want the second-smallest element of arr there is one final step.
n, i = a.last
#=> [1, 3]
n #=> 1
i #=> 3
Note that
n, i = [3, 2, 4, 2].each_with_index.min(2).last
#=> [2, 3]
n #=> 2
If we wanted n to equal 3 in this case we could write
n, i = [3, 2, 4, 2].uniq.each_with_index.min(2).last
#=> [3, 0]
n #=> 3
If the entries were floats or a mix of floats and integers we need only replace to_i with to_f.
str = "6.3, -1, 2.4, 3\n"
arr = str.split(/ *, */).map(&:to_f)
#=> [6.3, -1.0, 2.4, 3.0]
n, i = arr.each_with_index.min(2).last
#=> [2.4, 2]
n #=> 2.4
i #=> 2
Scan Input Strings and Map to Integers
The Kernel#gets method returns a String, so you have to parse and sanitize the user input to convert it to an Array. For example:
nums = gets.scan(/\d+/).map &:to_i
This uses String#scan to parse the input string, and Array#map to feed each element of the resulting array to String#to_i. The return value of this method chain will be an Array, which is then assigned to your nums variable.
Results of Example Data
Given input with inconsistent spacing or numbers of digits like:
1,2, 3, 4, 5, 10, 201
the method chain will nevertheless assign sensible values to nums. For example, the input above yields:
#=> [1, 2, 3, 4, 5, 10, 201]

How to generate partially repeated permutations in ruby?

I have a range of numbers R = (1..n). I also have another character 'a'. I want to generate strings of length L (L > n + 2) that have all the numbers in the same order, but go through every repeated permutation of 'a' to fill the length L. For example, if n = 3, and L = 7, then some valid strings would be :
"123aaaa",
"1a23aaa",
"1aa2a3a",
"aaaa123"
while the following strings would be invalid:
"213aaaa", # invalid, because 1,2,3 are not in order
"123a", #invalid, because length < L
"1123aaa", # invalid because a number is repeated
I am currently doing this, which is way too inefficient:
n = 3
L = 7
all_terms = (1..n).to_a + Array.new(L - n, 'a')
all_terms.permutation.each do |permut|
if(valid_permut? permut) # checks if numbers are in their natural order
puts permut.join
end
end
How do I directly generate valid strings more efficiently?
The problem is equivalent to: select n elements from index 0 to L - 1, fill these with 1 to n accordingly, and fill the rest with some constant character.
In your example, it's taking 3 elements from 0..6:
(0..6).to_a.combination(3).to_a
=> [[0, 1, 2], [0, 1, 3], [0, 1, 4], [0, 1, 5], [0, 1, 6], [0, 2, 3], [0, 2, 4],
[0, 2, 5], [0, 2, 6], [0, 3, 4], [0, 3, 5], [0, 3, 6], [0, 4, 5], [0, 4, 6], [0, 5, 6],
[1, 2, 3], [1, 2, 4], [1, 2, 5], [1, 2, 6], [1, 3, 4], [1, 3, 5], [1, 3, 6], [1, 4, 5],
[1, 4, 6], [1, 5, 6], [2, 3, 4], [2, 3, 5], [2, 3, 6], [2, 4, 5], [2, 4, 6], [2, 5, 6],
[3, 4, 5], [3, 4, 6], [3, 5, 6], [4, 5, 6]]
Every subarray here represents a possible result. For example, [0, 2, 3] corresponds to '0a12aaa', [3, 5, 6] corresponds to 'aaa0a12', etc. The code for this conversion is straight-forward.
You can model this as all possible interleavings of two strings, where relative order of the input elements is preserved. Here's a recursive solution. It works by choosing an element from one list, and prepending it to all possible subproblems, then doing it again where an element is chosen from the second list instead, and combining the two solution sets at the end.
# Returns an array of all possible interleaving of two strings
# Maintains relative order of each character of the input strings
def interleave_strings_all(a1, a2)
# Handle base case where at least one input string is empty
return [a1 + a2] if a1.empty? || a2.empty?
# Place element of first string, and prepend to all subproblems
set1 = interleave_strings_all(a1[1..-1], a2).map{|x| a1[0] + x}
# Place element of second string and prepend to all subproblems
set2 = interleave_strings_all(a1, a2[1..-1]).map{|x| a2[0] + x}
# Combine solutions of subproblems into overall problem
return set1.concat(set2)
end
if __FILE__ == $0 then
l = 5
n = 3
a1 = (1..n).to_a.map{|x| x.to_s}.join()
a2 = 'a' * (l - n)
puts interleave_strings_all(a1, a2)
end
The output is:
123aa
12a3a
12aa3
1a23a
1a2a3
1aa23
a123a
a12a3
a1a23
aa123

How do I explode an internal array in Ruby if the internal count is less than a certain value?

Using Ruby 2.1, if I have an array like:
[[1,1], [2,3], [5,8], [6, 4]]
How can I convert that to an array that only has internal arrays with a count > 3?
For example, it should be:
[1, 2, 2, 2, [5,8], [6,4]]
So [5,8] and [6,4] would "pass" because their counts are > 3 but [1,1] and [2,3] would "fail" and explode out because their counts are < than 4.
EDIT
Sorry, I wasn't very clear. By "counts" I mean the second value in the internal arrays. For example, the [2,3] would have a value of 2 and a count of 3. [5,8] would have a value of 5 and a count of 8.
So if the count is > 3 then keep the original array. If the count is 3 or less, then explode the value out count number of times.
I'm pretty sure someone can come up with a better way of doing this, but:
input = [[1,1], [2,3], [5,8], [6, 4]]
input.flat_map {|val, ct| ct > 3 ? [[val, ct]] : Array.new(ct, val) }
# => [1, 2, 2, 2, [5, 8], [6, 4]]
The basic idea is that we just map the inputs (each entry) to an output (the original entry or an exploded list of values) by the count. I'm using flat_map here, but you could use the same technique with map {}.flatten(1) if you wanted. You could also use inject or each_with_object to collect the output values, which may be more straightforward but slightly less terse.
Try this:
data = [[1,1], [2,3], [5,8], [6, 4]]
results = []
data.each do |arr|
val, count = arr
if count > 3
results << arr
else
results.concat [val] * count
end
end
p results
--output:--
[1, 2, 2, 2, [5, 8], [6, 4]]
arr = [[1,1], [2,3], [5,8], [6, 4]]
arr.flat_map { |a| (a.last > 3) ? [a] : [a.first]*a.last }
#=> [1, 2, 2, 2, [5, 8], [6, 4]]
Thanks to #ChrisHeald for pointing out that flat_map is equivalent to map {}.flatten(1) (I previously had the latter) and to #7stud for telling me my original solution was incorrect, which gave me the opportunity to make my solution more interesting as well as (hopefully) correct.

Sort Array of Arrays by length with tiebreaker

I have an Array of Arrays that I want to sort by longest length to shortest. I achieved this easily enough with a sort_by
> a = [ [1, 2, 9],
[4, 5, 6, 7],
[1, 2, 3] ]
> a.sort_by(&:length).reverse # or a.sort_by {|e| e.length}.reverse
=> [[4, 5, 6, 7], [1, 2, 3], [1, 2, 9]]
What I want, however is to have a sort of tie-breaker for lists of equal length. If two lists' lengths are equal, the list whose last entry is greater should come first. So in the above, [1, 2, 9] and [1, 2, 3] should be switched.
I don't care abouth the case where two lists have both equal length and equal last element, they can be in whatever order if that occurs. I don't know if/how I can acheive this with ruby built-in sorting.
You can still do this with sort_by, you just need to realize that Ruby arrays compare element-by-element:
ary <=> other_ary → -1, 0, +1 or nil
[...]
Each object in each array is compared (using the <=> operator).
Arrays are compared in an “element-wise” manner; the first two elements that are not equal will determine the return value for the whole comparison.
That means that you can use arrays as the sort_by key, then throw in a bit of integer negation to reverse the sort order and you get:
a.sort_by { |e| [-e.length, -e.last] }
That will give you the [[4, 5, 6, 7], [1, 2, 9], [1, 2, 3]] that you're looking for.
If you're not using numbers so the "negation to reverse the order" trick won't work, then use Shaunak's sort approach.
There you go :
a = [ [1, 2, 9],[4, 5, 6, 7],[1, 2, 3] ]
a.sort { |a, b| (b.count <=> a.count) == 0 ? (b.last <=> a.last): (b.count <=> a.count) }
That should give you:
[[4, 5, 6, 7], [1, 2, 9], [1, 2, 3]]
How this works: we pass a block to sort function, which first checks if the array length is same, if not it continues to check for last element.
You could use
a.sort_by {|i| [i.length, i.last] }.reverse
# => [[4, 5, 6, 7], [1, 2, 9], [1, 2, 3]]

Resources