My problem is that I have a given array of n numbers between 1 and 100. The goal is to pick 5 numbers which result in a minimum total distance. The total distance is calculated by summing up the distance of each number in the initial array to the closest of the 5 picked numbers.
What I (sort of) tried and thought about:
Taking the average number of the array and dividing it by 5 to get something useful?
Dividing the array length by 5, that numbers x and then the first number is array[x] the second one is array[x*2] and so on
Example
Input [5, 10, 15, 20, ..., 85, 90, 95, 100]
Output [10, 30, 50, 70, 90]
(There might be a better output but I hope this makes the goal clear)
As you can see I'm pretty lost and just can't come up with a solution. There probably is a super easy solution to this that I just don't get.
I am just looking for a hint not a solution, I wan't to figure that out myself.
Here is an algorithm that works in polynomial time.
First, sort your array of n things. Next, calculate a 2-dim array which for every 0 <= i <= j < n contains the index of the optimal element to fill the range from the ith element to the jth element. Fill out a similar array of the total distance for each interval from that optimal array.
As an example with the above sample output, the first 2-dim array could look like:
optimal_index = [
[ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9],
[ 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10],
[ 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10],
[ 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11],
[ 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11],
[ 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12],
[ 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12],
[ 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13],
[ 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13],
[ 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14],
[10, 10, 11, 11, 12, 12, 13, 13, 14, 14],
[11, 11, 12, 12, 13, 13, 14, 14, 15],
[12, 12, 13, 13, 14, 14, 15, 15],
[13, 13, 14, 14, 15, 15, 16],
[14, 14, 15, 15, 16, 16],
[15, 15, 16, 16, 17],
[16, 16, 17, 17],
[17, 17, 18],
[18, 18],
[19],
]
where the index of the optimal element for the range from i to j is at optimal_index[i][j-i]. With the same indexing scheme, the cost matrix would be:
optimal_cost = [
[ 0, 5, 10, 20, 30, 45, 60, 80, 100, 125, 150, 180, 210, 245, 280, 320, 360, 405, 450, 500],
[ 0, 5, 10, 20, 30, 45, 60, 80, 100, 125, 150, 180, 210, 245, 280, 320, 360, 405, 450],
[ 0, 5, 10, 20, 30, 45, 60, 80, 100, 125, 150, 180, 210, 245, 280, 320, 360, 405],
[ 0, 5, 10, 20, 30, 45, 60, 80, 100, 125, 150, 180, 210, 245, 280, 320, 360],
[ 0, 5, 10, 20, 30, 45, 60, 80, 100, 125, 150, 180, 210, 245, 280, 320],
[ 0, 5, 10, 20, 30, 45, 60, 80, 100, 125, 150, 180, 210, 245, 280],
[ 0, 5, 10, 20, 30, 45, 60, 80, 100, 125, 150, 180, 210, 245],
[ 0, 5, 10, 20, 30, 45, 60, 80, 100, 125, 150, 180, 210],
[ 0, 5, 10, 20, 30, 45, 60, 80, 100, 125, 150, 180],
[ 0, 5, 10, 20, 30, 45, 60, 80, 100, 125, 150],
[ 0, 5, 10, 20, 30, 45, 60, 80, 100, 125],
[ 0, 5, 10, 20, 30, 45, 60, 80, 100],
[ 0, 5, 10, 20, 30, 45, 60, 80],
[ 0, 5, 10, 20, 30, 45, 60],
[ 0, 5, 10, 20, 30, 45],
[ 0, 5, 10, 20, 30],
[ 0, 5, 10, 20],
[ 0, 5, 10],
[ 0, 5],
[ 0],
]
Now what about if we fill ranges with 2 elements? This is a question of taking each range, and looking the costs at each point we could divide it. That new data structure just needs to contain the places to separate between "closest to first element" and "closest to second". From this division we can take any range and quickly divide it into the optimal 2, then tell you what the two selected elements are, and the total cost. This can be filled in with a similar matrix. Note that the previous optimal_cost matrix will make these calculations very straightforward.
Next, what about ranges with 4 elements? This is exactly the same as ranges of 2 elements, except that we are now dividing between the first pair and the second pair. But the logic is the same.
And finally, what about our problem with 5 elements? That's just a question of calculating the optimal division between closest to the first 4 elements and closest to the last one. So just try all of the possibilities.
The natural generalization of this to filling k things in an array of size n is O(n^3 log(k)).
I got this question in an interview and got almost all the way to the answer but got stuck on the last part. If I want to get the multiplication table for 5, for instance, I want to get the output to be formatted like so:
1, 2, 3, 4, 5
2, 4, 6, 8, 10
3, 6, 9, 12, 15
4, 8, 12, 16, 20
5, 10, 15, 20, 25
My answer to this is:
def make_table(n)
s = ""
1.upto(n).each do |i|
1.upto(n).each do |j|
s += (i*j).to_s
end
s += "\n"
end
p s
end
But the output for make_table(5) is:
"12345\n246810\n3691215\n48121620\n510152025\n"
I've tried variations with array but I'm getting similar output.
What am I missing or how should I think about the last part of the problem?
You can use map and join to get a String in one line :
n = 5
puts (1..n).map { |x| (1..n).map { |y| x * y }.join(', ') }.join("\n")
It iterates over rows (x=1, x=2, ...). For each row, it iterates over cells (y=1, y=2, ...) and calculates x*y. It joins every cells in a row with ,, and joins every rows in the table with a newline :
1, 2, 3, 4, 5
2, 4, 6, 8, 10
3, 6, 9, 12, 15
4, 8, 12, 16, 20
5, 10, 15, 20, 25
If you want to keep the commas aligned, you can use rjust :
puts (1..n).map { |x| (1..n).map { |y| (x * y).to_s.rjust(3) }.join(',') }.join("\n")
It outputs :
1, 2, 3, 4, 5
2, 4, 6, 8, 10
3, 6, 9, 12, 15
4, 8, 12, 16, 20
5, 10, 15, 20, 25
You could even go fancy and calculate the width of n**2 before aligning commas :
n = 11
width = Math.log10(n**2).ceil + 1
puts (1..n).map { |x| (1..n).map { |y| (x * y).to_s.rjust(width) }.join(',') }.join("\n")
It outputs :
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22
3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33
4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44
5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55
6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66
7, 14, 21, 28, 35, 42, 49, 56, 63, 70, 77
8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88
9, 18, 27, 36, 45, 54, 63, 72, 81, 90, 99
10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110
11, 22, 33, 44, 55, 66, 77, 88, 99, 110, 121
Without spaces between the figures, the result is indeed unreadable. Have a look at the % operator, which formats strings and numbers. Instead of
s += (i*j).to_s
you could write
s += '%3d' % (i*j)
If you really want to get the output formatted in the way you explained in your posting (which I don't find that much readable), you could do a
s += "#{i*j}, "
This leaves you with two extra characters at the end of the line, which you have to remove. An alternative would be to use an array. Instead of the inner loop, you would have then something like
s += 1.upto(n).to_a.map {|j| i*j}.join(', ') + "\n"
You don't need to construct a string if you're only interested in printing the table and not returning the table(as a string).
(1..n).each do |a|
(1..n-1).each { |b| print "#{a * b}, " }
puts a * n
end
This is how I'd do it.
require 'matrix'
n = 5
puts Matrix.build(n) { |i,j| (i+1)*(j+1) }.to_a.map { |row| row.join(', ') }
1, 2, 3, 4, 5
2, 4, 6, 8, 10
3, 6, 9, 12, 15
4, 8, 12, 16, 20
5, 10, 15, 20, 25
See Matrix::build.
You can make it much shorter but here's my version.
range = Array(1..12)
range.each do |element|
range.map { |item| print "#{element * item} " } && puts
end
Hi I have the following simple program:
joint = Table[0, {i, Length[labelnames]}, {j, 16}];
For[time = 1,
time < Length[topics], time++
Do[
joint[[l, t]]++, {l, labelsForTime[time]}, {t, topics[[time]]}
]
]
Result of which, joint is:
{{0, 1267, 90, 0, 0, 58, 1358, 2, 25, 1, 0, 0, 6, 0, 2585,
0}, (7507 + List)[111, 773, 3302, 8092, 405, 1776, 4203, 153, 9551,
118, 9, 2260, 17, 665, 5586, 0], (3288 + List)[0, 43, 46, 716, 0,
120, 20, 2, 576, 0, 0, 246, 0, 0, 118, 0], (382 + List)[7, 80, 191,
87, 1, 38, 2887, 3, 1967, 0, 5, 72
....
Notice the (7505 + List), (3288 + List) .. and other similar elements in the output. I just can't figure out what these are, and how they got into joint, which is a simple list of lists.
Aren't you missing a comma after time++? (I can't run your code because there are too many unknown variables...)