Related
We have the following array:
[4, 13, 25, 33, 38, 41, 55, 71, 73, 84, 86, 92, 97]
To me it seems like there are only 3 comparisons needed to find 25, because:
First we pick the middle element 55. Now we perform two comparisons: 55 = 25? 55 > 25? None of these hold so we go to the left of the array. We get the subarray: [4, 13, 25, 33, 38, 41]
We divide this again and get 25 = 25? yes.. So it took 3 comparisons to get our match. My book says there are four comparisons needed to find 25. Why is this?
As the size of the left array is even, each algorithm could select one of the middle numbers. Hence, the comparison could be like the following with 4 comparison:
[4, 13, 25, 33, 38, 41, 55, 71, 73, 84, 86, 92, 97]
25 < 55 => [4, 13, 25, 33, 38, 41]
25 < 33 => [4, 13, 25]
25 > 13 => [25]
25 == 25 => Found.
iex> MapSet.new(1..32) |> Enum.to_list
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, 26, 27, 28, 29, 30, 31, 32]
iex> MapSet.new(1..33) |> Enum.to_list
[11, 26, 15, 20, 17, 25, 13, 8, 7, 1, 32, 3, 6, 2, 33, 10, 9, 19, 14, 5, 18, 31,
22, 29, 21, 27, 24, 30, 23, 28, 16, 4, 12]
Here's the implementation in Elixir 1.3
def new(enumerable) do
map =
enumerable
|> Enum.to_list
|> do_new([])
%MapSet{map: map}
end
defp do_new([], acc) do
acc
|> :lists.reverse
|> :maps.from_list
end
defp do_new([item | rest], acc) do
do_new(rest, [{item, true} | acc])
end
Even though the order doesn't matter in a MapSet, but still wondering why a MapSet becomes unordered after 32 elements?
This is not specific to MapSet, but the same thing happens with normal Map (MapSet uses Map under the hood):
iex(1)> for i <- Enum.shuffle(1..32), into: %{}, do: {i, i}
%{1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5, 6 => 6, 7 => 7, 8 => 8, 9 => 9,
10 => 10, 11 => 11, 12 => 12, 13 => 13, 14 => 14, 15 => 15, 16 => 16,
17 => 17, 18 => 18, 19 => 19, 20 => 20, 21 => 21, 22 => 22, 23 => 23,
24 => 24, 25 => 25, 26 => 26, 27 => 27, 28 => 28, 29 => 29, 30 => 30,
31 => 31, 32 => 32}
iex(2)> for i <- Enum.shuffle(1..33), into: %{}, do: {i, i}
%{11 => 11, 26 => 26, 15 => 15, 20 => 20, 17 => 17, 25 => 25, 13 => 13, 8 => 8,
7 => 7, 1 => 1, 32 => 32, 3 => 3, 6 => 6, 2 => 2, 33 => 33, 10 => 10, 9 => 9,
19 => 19, 14 => 14, 5 => 5, 18 => 18, 31 => 31, 22 => 22, 29 => 29, 21 => 21,
27 => 27, 24 => 24, 30 => 30, 23 => 23, 28 => 28, 16 => 16, 4 => 4, 12 => 12}
This is because (most likely as an optimization) Erlang stores Maps of size upto MAP_SMALL_MAP_LIMIT as a sorted by key array. Only after the size is greater than MAP_SMALL_MAP_LIMIT Erlang switches to storing the data in a Hash Array Mapped Trie like data structure. In non-debug mode Erlang, MAP_SMALL_MAP_LIMIT is defined to be 32, so all maps with length upto 32 should print in sorted order. Note that this is an implementation detail as far as I know, and you should not rely on this behavior; they may change the value of the constant in the future or switch to a completely different algorithm if it's more performant.
Why is the include method not working well? The original question is from the Euler project, Problem 23. I couldn't figure out how to debug it.
My code:
def proper_divisors(n)
(1...n).select {|x| n % x == 0 }.inject(0){|x,y| x + y}
end
def abundant?(n)
(1...n).select {|x| n % x == 0 }.inject(0){|x,y| x + y} > n
end
def non_abundant_sums
s = 0
arr = (12..40).select { |n| n if abundant?(n) }
p arr
(1..40).each do |x|
p x unless arr.include?(proper_divisors(x) - x)
s = s + x unless arr.include?(proper_divisors(x) - x)
end
s
end
p non_abundant_sums
Using p x unless arr.include?(proper_divisors(x) - x) in the above code prints 1 through 40:
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 25, 26, 27, 28, 29, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40
What I want it to print is 1 through 39:
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 25, 26, 27, 28, 29, 31, 33, 34, 35, 37, 39
A solution based on the original methods from the example.
In file problem_23.rb:
def proper_divisors(n)
(1...n).select {|x| n % x == 0 }.inject(0) {|x,y| x + y}
end
def abundant?(n)
proper_divisors(n) > n
end
def non_abundant_sum(low_n, high_n, debug=false)
puts "get all the abundant numbers within range #{low_n} to #{high_n}" if debug
arr = (low_n..high_n).select {|n| n if abundant?(n)}
puts arr.sort.inspect if debug
# http://ruby-doc.org/core-2.1.2/Array.html#method-i-repeated_combination
puts "all combinations of two abundant numbers" if debug
arr = arr.repeated_combination(2).to_a
puts arr.inspect if debug
puts "all unique sums of two abundant number combinations" if debug
arr = arr.map {|x| x[0] + x[1]}.uniq
puts arr.sort.inspect if debug
puts "only select numbers within range" if debug
arr = arr.select {|x| low_n <= x && x <= high_n}
puts arr.inspect if debug
puts "all positive integers within range" if debug
arr2 = (low_n..high_n).map {|i| i}
puts arr2.inspect if debug
puts "all positive integers less all the sums of two abundant numbers" if debug
arr = arr2 - arr
puts arr.inspect if debug
puts "sum of all the positive integers which cannot be written as the sum of two abundant numbers within range #{low_n} to #{high_n}" if debug
arr.inject(0) {|sum,n| sum + n}
end
puts non_abundant_sum(12, 40, true)
Running the code:
$ ruby problem_23.rb
get all the abundant numbers within range 12 to 40
[12, 18, 20, 24, 30, 36, 40]
all combinations of two abundant numbers
[[12, 12], [12, 18], [12, 20], [12, 24], [12, 30], [12, 36], [12, 40], [18, 18], [18, 20], [18, 24], [18, 30], [18, 36], [18, 40], [20, 20], [20, 24], [20, 30], [20, 36], [20, 40], [24, 24], [24, 30], [24, 36], [24, 40], [30, 30], [30, 36], [30, 40], [36, 36], [36, 40], [40, 40]]
all unique sums of two abundant number combinations
[24, 30, 32, 36, 38, 40, 42, 44, 48, 50, 52, 54, 56, 58, 60, 64, 66, 70, 72, 76, 80]
only select numbers within range
[24, 30, 32, 36, 38, 40]
all positive integers within range
[12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40]
all positive integers less all the sums of two abundant numbers
[12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 25, 26, 27, 28, 29, 31, 33, 34, 35, 37, 39]
sum of all the positive integers which cannot be written as the sum of two abundant numbers within range 12 to 40
554
#bingo_board = Array.new(5) {Array(5.times.map{rand(1..100)})}
B I N G O
0 1 2 3 4
0 1 2 3 4
0 1 2 3 4
0 1 2 3 4
0 1 2 3 4
I am trying to get the elements in the sub arrays of #bingo_board to position vertically, but with each array horizontally aligned.
EDIT
Maybe I missunderstood the question, to transpose printing the array (if this what you want), just use print by first loop, then the second:
bb = [[ 4, 2, 74, 97, 76],
[67, 54, 61, 84, 31],
[68, 56, 49, 57, 69],
[61, 92, 71, 77, 22],
[99, 8, 89, 11, 88]] # just an example
puts ('%3s ' * 5) % 'BINGO'.split('')
5.times do |y|
5.times { |x| print "%3i " % bb[x][y] }
# access by x (column) then y (row)
# normally its by row first then column
puts
end
# the output:
B I N G O
4 67 68 61 99
2 54 56 92 8
74 61 49 71 89
97 84 57 77 11
76 31 69 22 88
# just for knowledge, when accessed by row then column first (bb[y][x]), the output would be:
4 2 74 97 76
67 54 61 84 31
68 56 49 57 69
61 92 71 100 22
99 8 89 11 88
OLD ANSWER
Just use map to fetch all columns, for example:
def get_col bb, y
bb.map{|x| x[y]}
end
# test:
bingo_board = Array.new(5) {Array(5.times.map{rand(1..100)})}
#=> [[70, 4, 31, 46, 91],
# [76, 47, 16, 80, 37],
# [61, 72, 7, 10, 49],
# [44, 62, 98, 54, 86],
# [90, 43, 45, 85, 57]]
get_col bingo_board, 0
#=> [70, 76, 61, 44, 90]
get_col bingo_board, 1
#=> [4, 47, 72, 62, 43]
get_col bingo_board, 2
#=> [31, 16, 7, 98, 45]
get_col bingo_board, 3
#=> [46, 80, 10, 54, 85]
get_col bingo_board, 4
#=> [91, 37, 49, 86, 57]
It's always better to create a class to do so:
class BingoBoard
N = 5
def initialize
#board = Array.new(N) {Array(N.times.map{rand(1..100)})}
end
def get_col n
raise "invalid column: #{n}" if n<0 or n>=N
#board.map{|x| x[n]}
end
def get_row n
raise "invalid row: #{n}" if n<0 or n>=N
#board[n]
end
end
# test:
bb = BingoBoard.new
#=> #<BingoBoard:0x0000000215f6b0 #board=[
# [ 4, 8, 96, 49, 30],
# [21, 49, 43, 73, 96],
# [83, 73, 10, 27, 54],
# [51, 89, 10, 31, 20],
# [47, 58, 44, 78, 18]]>
bb.get_row 0
#=> [4, 8, 96, 49, 30]
bb.get_col 0
#=> [4, 21, 83, 51, 47]
bb.get_col 4
#=> [30, 96, 54, 20, 18]
This question already has answers here:
Finding three elements in an array whose sum is closest to a given number
(15 answers)
Closed 9 years ago.
I need to create a combination of sum of values closest to the target value. The combination needs to be of x numbers, where x is defined by the user. The algorithm will output the combination closest to a target value entered by the user. I also need to display the keys (values) that the algorithm returns.
Here is how I think the algorithm will work:
Target: 575
Values with corresponding keys:
150 [0] | 75 [1] | 123 [2] | 212 [3] | 23 [4] | 89 [5] | 20 [6]
77 [7] | 39 [8] | 16 [9] | 347 [10] | 512 [11] | 175 [12]
User wants Groups of: 5 values
The algorithm now runs combinations of sum of 5 values on the whole set and returns a sum of the values closest to the target value of 575.
Result
150 [0] + 212 [3] + 23 [4] + 77 [7] + 89 [5] = 551
Keys used were 0, 3, 4, 7, and 5.
I could use Arrays#combination(n), but I will not be able to keep track of the keys. I have been able to come up with a Hash which stores "key" => "int values", but I have no idea how to come up with an optimized algorithm to combine values stored in a Hash.
{0=>"150"}
{1=>"212"}
{2=>"23"}
{3=>"77"}
{4=>"89"}
P.S. This is not a homework. Its a personal project to put on the resume, talk about at interviews, and to learn to convert my ideas to code.
Something like this would work:
hash = {0 => 150, 1 => 75, 2 => 123, 3 => 212, 4 => 23, 5 => 89, 6 => 20, 7 => 77, 8 => 39, 9 => 16, 10 => 347, 11 => 512, 12 => 175}
# all key combinations of length 5
keys_of_5 = hash.keys.combination(5)
#=> #<Enumerator: [0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12]:combination(5)>
# i.e. [[0, 1, 2, 3, 4], [0, 1, 2, 3, 5], [0, 1, 2, 3, 6], ...]
# sum the values for each combination
sums_of_5 = keys_of_5.map { |keys| [keys, hash.values_at(*keys).inject(:+)] }
#=> [[[0, 1, 2, 3, 4], 583], [[0, 1, 2, 3, 5], 649], [[0, 1, 2, 3, 6], 580], ...]
# sort by distance to target
sorted = sums_of_5.sort_by { |keys, sum| (sum - 575).abs }
#=> [[[4, 5, 7, 8, 10], 575], [[0, 4, 8, 9, 10], 575], [[3, 4, 5, 7, 12], 576], ...]
# let's find the nearest ones
nearest = sorted.select { |keys, sum| sum == sorted.first[1] }
# and print 'em
nearest.each do |keys, sum|
puts keys.map { |key| "%3d [%d]" % [hash[key], key] }.join(" + ") << " = #{sum}"
end
Output
23 [4] + 89 [5] + 77 [7] + 39 [8] + 347 [10] = 575
150 [0] + 23 [4] + 39 [8] + 16 [9] + 347 [10] = 575
In order to keep track of the indice, you can apply combination on the indice of the array, not the array itself.
array = [150, 75, 212, 23, 89, 20, 77, 39, 16, 347, 512, 175]
target = 575
x = 5
closest_indice =
array
.each_index.to_a
.combination(x)
.min_by{|is| (array.values_at(*is).inject(:+) - target).abs}
However, the answer is different from what you claim:
closest_indice # => [0, 3, 7, 8, 9]
array.values_at(*closest_indice) # => [150, 23, 39, 16, 347]
array.values_at(*closest_indice).inject(:+) # => 575
and I don't understand why you have a different answer.
Edit
As noticed my Stefan, there is no index 2. To deal with that:
hash = {0 => 150, 1 => 75, 3 => 212, 4 => 23, 5 => 89, 6 => 20, 7 => 77, 8 => 39, 9 => 16, 10 => 347, 11 => 512, 12 => 175}
target = 575
x = 5
closest_keys =
hash
.keys
.combination(x)
.min_by{|is| (hash.values_at(*is).inject(:+) - target).abs}
closest_keys # => [0, 4, 8, 9, 10]
hash.values_at(*closest_indice) # => [150, 23, 39, 16, 347]
hash.values_at(*closest_indice).inject(:+) # => 575
Notice This answer applies to the question as was at the beginning (i.e., before the OP changed the question to add an element 123 with index 2).