How to calculate user inputted data from a for loop - ruby

numbers = [1, 2, 3, 4, 5]
for num in numbers
puts "insert q "
qanswer1 = gets.chomp.to_i
puts "insert 2nd q"
qanswer2 = gets.chomp.to_i
end
This code will repeat the questions 5 times. How do I retrieve the user-inputted data? I'm trying to calculate the total sum of all 10 responses and the index in which the sum of each pair was the highest.

nbr_pairs = 5
def ask(str)
print "#{str}: "
gets.to_i
end
arr = nbr_pairs.times.map { [ask("insert q"), ask("insert 2nd q")] }
#=> [[7, 3], [8, 6], [2, 9], [4, 6], [8, 3]]
Sum of all 10 replies
arr.flatten.sum
#=> 56
Index of largest sum of pairs
nbr_pairs.times.max_by { |i| arr[i].sum }
#=> 1 (8+6 = 14)
See Integer#times, Array#flatten, Enumerable#max_by and Array#sum.

I tried to make a solution as close as possible to yours while still producing the desired results :
numbers = [0,1,2,3,4]
pairs = Array.new(numbers.size, [])
numbers.each do |number|
pairs[number] = []
puts "q 1:"
pairs[number] << gets.to_i
puts "q 2:"
pairs[number] << gets.to_i
end
To get the pair with the max value:
max_value = pairs.max_by(&:sum)
To get the sum of all pairs:
sum_of_all_responses = pairs.flatten.sum
To get the index of the pair with the highest value:
pairs.index(max_value)

If you don't need to store all the data, you could just do calculations on the fly.
First move the input request to a method which returns the pair of value n an array:
def ask
puts "insert q "
qanswer1 = gets.to_i
puts "insert 2nd q"
qanswer2 = gets.to_i
[qanswer1, qanswer2]
end
Then set up variables
big_sum = 0
best_input = 0
best_input_q_number = nil
Finally loop as you did doing the math:
numbers = [1,2,3,4,5]
for n in numbers
# (1..5).each do |n| # this is more Ruby-ish
p qanswers = ask # p for debug
qanswers_sum = qanswers.sum
big_sum += qanswers_sum
if qanswers_sum > best_input
best_input = qanswers_sum # or qanswers if you want to keep the pairs
best_input_q_number = n
end
end

Related

How to sort by value in hash?

So I made a random number generator which is supposed to count the frequency of the numbers and display them in sorted order. I'm trying to use .sort but I can't figure out where to put it to sort the values of the hash in order. What I have so far:
MIN_VALUE = 1
count = 0
puts "Enter a number of random integers to generate"
resp = gets.to_i
p "number of integers generated is #{resp}"
puts "Now enter the maximum value the integers can be"
max_value = gets.to_i
p "max value is set to #{max_value}"
size = Array.new(resp)
while (count < resp)
int_value = (rand(MIN_VALUE..max_value))
size.push(int_value)
count = count + 1
end
puts size
freq = Hash.new(0)
size.each { |x| freq[x] += 1 }
freq.map{ |key, value| "#{key}x#{value}" }.join(',')
freq.each do |key,value|
puts "Frequency of #{key} is: #{value}"
end
Any help is greatly appreciated!
More or less the same soup, generating random numbers in an Integer#times loop:
upper_number = 10
sample_size = 100
freq = Hash.new(0) # initializing the hash with a default value of zero, for counting
sample_size.times { freq[rand((1..upper_number))] += 1 } # here the loop generating and counting
freq #=> {5=>13, 7=>10, 1=>11, 2=>13, 8=>13, 9=>6, 3=>6, 6=>9, 10=>11, 4=>8}
Then you can sort by frequencies (reverse order: -v) and by sample value (k), [-v, k]:
freq.sort_by{ |k, v| [-v, k] }.to_h #=> {2=>13, 5=>13, 8=>13, 1=>11, 10=>11, 7=>10, 6=>9, 4=>8, 3=>6, 9=>6} # for this run
freq.sum { |_, v| v} #=> 100 # of course
Suppose
arr = [4, 1, 3, 4, 2, 5, 1, 3, 4, 3, 4]
You can use the form of Hash::new that takes an argument, called its default value (which often, as here, is zero), to obtain the frequency of the elements of arr:
freq = arr.each_with_object(Hash.new(0)) { |n,h| h[n] += 1 }
#=> {4=>4, 1=>2, 3=>3, 2=>1, 5=>1}
We see that
freq[1]
#=> 2
freq[99]
#=> 0
The second result follows because freq was defined to have a default value of 0. All that means is that if freq does not have a key k, freq[k] returns zero (and that does not alter freq).
Here are solutions to two possible interpretations of your question. Both use the method Enumerable#sort_by.
Sort the unique values of arr by decreasing frequency
freq.sort_by { |_,v| -v }.map(&:first)
#=> [4, 3, 1, 2, 5]
Sort the values of arr by decreasing frequency
arr.sort_by { |n| -freq[n] }
#=> [4, 4, 4, 4, 3, 3, 3, 1, 1, 2, 5]
Replace -v and -freq[n] with v and freq[n] to sort by increasing frequency.
I've used the local variable _ to represent the keys in the first interpretation to signify that it is not used in the block calculation. This is common practice.

Ruby code looping infinitely

class Triplet
def initialize(array,sum)
#array = array.sort()
#array_size = array.size()
#sum = sum
#result = []
end
def get_triplet
#array[0..-3].each_with_index do |arr, ind|
pointer_one = ind + 1
pointer_two = #array_size - 1
while (pointer_one < pointer_two)
temp_sum = #array[pointer_one] + #array[pointer_two] + arr
if(temp_sum == #sum)
#result.push([#array[pointer_one], #array[pointer_two], arr])
elsif temp_sum < #sum
pointer_one = pointer_one +1
else
pointer_two = pointer_two -1
end
end
end
end
def get_result
#result.each do |res|
puts res
end
end
end
puts "Enter the array of numbers"
array = gets.chomp
array = array.split(' ')
array_integer = array.map{|a| a.to_i}
puts array_integer
puts "Enter the sum"
sum = gets.chomp
puts sum
t1 = Triplet.new(array_integer,sum.to_i)
t1.get_triplet
t1.get_result
Can anyone suggest me the fix so that it doesn't loop infinitly. It is program to find triplet in array whose sum is #sum. Its looping in get_triplet method. Initialize method sets the array,array size. get_triplet method should store all three number whose sum is #sum in result array.
Usually a tangle of code like this is a sign something's not right, and in this case the source of the problem is not knowing about the combination method. Here's a functionally equivalent solution:
def triplet(list, target)
list.combination(3).find do |a,b,c|
a + b + c == target
end
end
For example:
arr = [ 1, 2, 3, 4, 5, 6, 7, 8 ]
p triplet(arr, 6)
# => [1, 2, 3]
p triplet(arr, 4)
# => nil
p triplet(arr, 10)
# => [1, 2, 7]
The algorithm used in your code looks problematic, or at least implemented incorrectly, and is also strictly limited to triplets. This code is far more generic and uses a proven, tested algorithm, so it's probably better suited to solving your particular problem.

How to iterate through array using nested while loops in Ruby?

In my code below it seems that the i variable in the 2nd while loop is not incrementing within the 2nd while loop, but it is incrementing in the first while loop. So i is always equal to zero in the 2nd while loop, however I need it to increment +1 each pass.
Here is my code:
# Code required to read in the values of k,n and candies.
n = gets.to_i
k = gets.to_i
candy = Array.new(n)
for i in 0..n-1
candy[i] = gets.to_i
end
#create loop to calculate max-min and compare to lowest max-min value calculated thus far.
arr = []
i = 0
candy = candy.sort
ans = nil
#iterate through candy array to create n minus k sub-arrays of size k
while i < n-k
m = 0
#create sub-array of size k
while m < k
arr << candy[i + m].to_i
m += 1
end
#find the difference between the max and the min value in the sub-array
arrcheck = (arr[k-1]) - (arr[0])
i += 1
#if ans is nil set the ans variable to arrcheck, else if arrcheck is less than the ans set ans to arrcheck
if ans == nil
ans = arrcheck
elsif arrcheck < ans
ans = arrcheck
end
end
### Compute answer from k, n, candies
puts ans
Since the i in the 2nd loop is not incrementing it is just checking the same sub-array n minus k times without advancing through the entire candy array.
I see two mistakes in your code:
while i < n - k should be while i <= n - k
arr = [] must be moved into the while loop
Fixed code:
while i <= n - k
m = 0
arr = []
# ...
Although your code works with the above fix, it's not very idiomatic. I'd write something like:
print 'number of candies: '
n = gets.to_i
print 'sub-array size: '
k = gets.to_i
candies = []
(1..n).each do |i|
print "candy #{i}: "
candies << gets.to_i
end
puts candies.sort.each_cons(k).map { |a| a.last - a.first }.min
Let's examine the last line:
candies = [1, 7, 10, 2]
k = 2
candies #=> [1, 7, 10, 2]
.sort #=> [1, 2, 7, 10]
.each_cons(k) #=> [[1, 2], [2, 7], [7, 10]]
.map { |a| a.last - a.first } #=> [1, 5, 3]
.min #=> 1

Checking to see if 2 numbers in array sum to 0 in Ruby

I've been going at this problem for a few hours, and I can't see why I can't get it to run properly. The end game to this method is having 2 numbers in an array equaling zero when added together. Here is my code:
def two_sums(nums)
i = 0
j = -1
while i < nums.count
num_1 = nums[i]
while j < nums.count
num_2 = nums[j]
if num_1 + num_2 == 0
return "There are 2 numbers that sum to zero & they are #{num_1} and #{num_2}."
else
return "Nothing adds to zero."
end
end
i += 1
j -= 1
end
end
The problem I'm having is unless the first and last number in the array are the positive and negative of the same number, this will always return false.
For example, if I had an array that was [1, 4, 6, -1, 10], it should come back true. I'm sure my 2 while statement is the cause of this, but I can't think of a way to fix it. If someone could point me in the right direction, that would be helpful.
You can find the first pair that adds up to 0 like this:
nums.combination(2).find { |x, y| x + y == 0 }
#=> returns the first matching pair or nil
Or if you want to select all pairs that add up to 0:
nums.combination(2).select { |x, y| x + y == 0 }
#=> returns all matching pairs or an empty array
Therefore you can implement your method like this:
def two_sums(nums)
pair = nums.combination(2).find { |x, y| x + y == 0 }
if pair
"There are 2 numbers that sum to zero & they are #{pair.first} and #{pair.last}."
else
"Nothing adds to zero."
end
end
Or if you want to find all pairs:
def two_sums(nums)
pairs = nums.combination(2).select { |x, y| x + y == 0 }
if pairs.empty?
"Nothing adds to zero."
else
"The following pairs sum to zero: #{pairs}..."
end
end
Here's another way:
Code
def sum_to_zero(arr)
arr.group_by { |e| e.abs }
.values
.select { |a| (a.size > 1 && a.first == 0) || a.uniq.size > 1 }
end
Examples
sum_to_zero [1, 4, 6, -1, 10] #=> [[1, -1]]
sum_to_zero [1, 4, 1, -2, 10] #=> []
sum_to_zero [1, 0, 4, 1, 0, -1] #=> [[1, 1, -1], [0, 0]]
This method is relatively fast. Let's try it with an array of 200,000 elements, each a random number between -500,000 and 500,000.
require 'time'
t = Time.now
arr = Array.new(200_000) { rand(1_000_001) - 500_000 }
arr.size #=> 200000
sum_to_zero(arr).size #=> 16439
Time.now - t
#=> 0.23 (seconds)
sum_to_zero(arr).first(6)
#=> [[-98747, 98747],
# [157848, -157848],
# [-459650, 459650],
# [176655, 176655, -176655],
# [282101, -282101],
# [100886, 100886, -100886]]
If you wish to group the non-negative and negative values that sum to zero:
sum_to_zero(arr).map { |a| a.partition { |e| e >= 0 } }.first(6)
#=> [[[98747], [-98747]],
# [[157848], [-157848]],
# [[459650], [-459650]],
# [[176655, 176655], [-176655]],
# [[282101], [-282101]],
# [[100886, 100886], [-100886]]]
If you only want a single value for each group (a non-negative value, say):
sum_to_zero(arr).map { |a| a.first.abs }.first(6)
#=> [98747, 157848, 459650, 176655, 282101, 100886]
I think the most Ruby way would be:
nums.combination(2).any? { |x,y| (x+y).zero? }
Here's a way that should work well for large arrays. The methods above which go through every possible combination of two numbers are perfectly fine for small cases but will be very slow and memory hungry for arrays with lots of elements.
def two_sums nums
h = Hash.new
nums.each do |n|
return true if h[-n]
h[n] = true
end
false
end
Well, given it's tagged as #ruby, here's the most "ruby way" I could think of tackling this problem:
def two_sums(arr)
numbers = arr.combination(2).select { |a| a.reduce(:+) == 0 }.flatten
if numbers.empty?
"Nothing adds to zero."
else
"There are 2 numbers that sum to zero & they are #{numbers.first} and #{numbers.last}."
end
end
array.combination(2).select{|x|x[0] + x[1] == 0}

Get all combinations of six players

I have an array of six players. This means I have fifteen unique games:
players = [1, 2, 3, 4, 5, 6]
games = players.combination(2).to_a
# => [[1, 2], [1, 3], [1, 4], [1, 5], [1, 6], [2, 3], [2, 4], [2, 5],
# [2, 6], [3, 4], [3, 5], [3, 6], [4, 5], [4, 6], [5, 6]]
I want to arrange these games randomly into 5 rounds of 3. Each player should play 1 game each round, and no pair should repeat from any previous round.
In each round, I have tried picking player1 and player2 randomly, using while loop coupled with each, but I always end up in infinite loop. Any suggestions?
So here is the code (sorry for not putting it in eariler). Problem is that sometimes it does work and gives me the result that I want but sometimes it just breaks down and it's caught in the loop.
def pick_pair(players)
player1 = players[rand(players.length)]
players.delete(player1)
player2 = players[rand(players.length)]
players.delete(player2)
pair = [player1, player2]
return pair.sort!
end
def check_round(all_rounds, current_round)
repeat = false
if all_rounds == []
repeat = false
else
repeat = catch :repeat do
k = 0
all_rounds.each do |round|
a_r_l = all_rounds.length
round.each do |pair|
r_l = round.length
k += 1
z = 0
current_round.each do |new_pair|
#p "Comparing: #{pair} to #{new_pair}"
z += 1
if pair == new_pair
repeat = true
throw :repeat, repeat
elsif (k == a_r_l*r_l and z == current_round.length and pair != new_pair)
repeat = false
throw :repeat, repeat
end
end
end
end
end
end
return repeat
end
players = [1,2,3,4,5,6]
all_rounds = []
for i in 1..(players.length-1)
#p "Round: #{i}"
players_d = players.dup
current_round = catch :round do
check = true
while check
current_round = []
for j in 1..(players.length/2)
#p "Game: #{j}"
pair = pick_pair(players_d)
current_round << pair
end
p "Previous rounds: #{all_rounds}"
p "Current round: #{current_round}"
repeat = check_round(all_rounds, current_round)
if repeat == false
throw :round, current_round
else
players_d = players.dup
end
end
end
all_rounds << current_round
end
Hey,
thanks for the help. I rewrote the code and it seems to works. It's also much simpler:
players = [1,2,3,4,5,6]
possible_games = players.combination(2).to_a
all_games = []
for i in 1..(players.length - 1)
round = catch :round do
check = true
while check
round = []
for i in 1..(players.length/2)
pair = possible_games[rand(possible_games.length)]
round << pair
end
if round.flatten.uniq == round.flatten
round.each do |game|
possible_games.delete(game)
end
throw :round, round
else
end
end
end
all_games << round.sort!
end
p all_games
Basically you are trying for random combinations and retrying until the scheme is valid. It will succeed now and then, but it will get in a deadlock sometimes:
Round 1 2 3 4 5
12 14 16 13
34 36 32 5??
56 52 54
In round 4, player 5 must play player 1 or 3, but that is not going to happen because 3 is already playing 1. Your script keeps retrying until the end of times.
Such a tournament is called a round-robin tournament; wikipedia has an algorithm.
Although, strictly speaking, the best (most efficient) solution here would be to use a "round-robin" algorithm as suggested by steenslang.
I would suggest a simpler approach which works great and is quite straight forward.
Basically, the program tries to make each round have each player playing once per round. It loops over a shuffled array of combinations and tries pairing them up. If the combination is not possible, it reshuffles the array and tries again.
It's simple and it works great.
Plus, you can change the number of players if you need to too and it just works.
Hope this helps:
players = [1, 2, 3, 4, 5, 6]
initial_games = players.combination(2).to_a.shuffle
players_sum = players.inject{|sum,x| sum + x }
all_rounds = []
games = initial_games
until games.empty?
current_round = []
games.each do |game|
if (current_round.flatten & game).empty?
current_round << game
end
end
current_round_sum = current_round.flatten.inject{|sum,x| sum + x }
if current_round_sum == players_sum
all_rounds << current_round
games = games - current_round
else
all_rounds = []
games = initial_games.shuffle
end
end
puts all_rounds.inspect # this outputs 5 rounds of 3 games

Resources