Find exact value overlap between two arrays [duplicate] - ruby

This question already has answers here:
Ruby - array intersection (with duplicates)
(7 answers)
Closed 1 year ago.
I have two arrays
a1 = [1, 1, 1, 2, 3, 3, 3]
a2 = [1, 1, 3, 3, 5, 5]
I want to return the values that appear in both arrays AND the exact amount that they appear
# => [1, 1, 3, 3]
I can't use a1 & a2 because that will return unique values ([1, 3])
What's the best way to achieve this?

It looks like what you have there are not really arrays, they are multisets or bags.
There is a general rule in programming: if you choose your data representation right, your algorithms become simpler.
So, if you use multisets instead of arrays, your problem will become trivial, since what you are looking for is literally just the intersection of two multisets.
Unfortunately, there is no multiset implementation in the core or standard libraries, but there are a couple of multiset gems available on the web. For example, there is the multimap gem, which also includes a multiset. Unfortunately, it needs a little bit of love and care, since it uses a C extension that only works until YARV 2.2. There is also the multiset gem.
require 'multiset'
m1 = Multiset.new(a1)
#=> #<Multiset:#3 1, #1 2, #3 3>
m2 = Multiset.new(a2)
#=> #<Multiset:#2 1, #2 3, #2 5>
m = m1 & m2
#=> #<Multiset:#2 1, #2 3>
Personally, I am not too big a fan of the inspect output, but we can see what's going on and that the result is correct: m contains 2 × 1 and 2 × 3.
If you really need the result as an Array, you can use Multiset#to_a:
m.to_a
#=> [1, 1, 3, 3]

At first I do get the intersection between a1 and a2 with (a1 & a2). After that I iterate over the intersection and check which array has a lower count of each element. The element gets than added to the result array as many times as it occurs in the array with the lower count using result.fill
a1 = [1, 1, 1, 2, 3, 3, 3]
a2 = [1, 1, 3, 3, 5, 5]
result = []
(a1 & a2).each do |e|
a1.count(e) < a2.count(e) ? result.fill(e, result.size, a1.count(e)) : result.fill(e, result.size, a2.count(e))
end
pp result

Related

How do you sort a list with a comparison function where values for a & b are known ahead of time?

I'm writing a simple interpreter that people can use (among other things) to sort a list via a comparison function (preferably a stable sort). Sorting algorithms that I'm familiar with all seem to require a variable number of calls to that comparison function and you don't know ahead of time which items will be compared to each other. That won't work because of the nature of what work is done in the interpreted language vs the runtime at what times.
The steps required by the interpreter are:
Step 1: Create a list of as to be compared to another list of bs, one a & b at a time. Something like sort([1, 2, 3]) producing:
a = [2, 3]
b = [1, 2]
(2 is compared to 1 and then 3 is compared to 2 in the above example, going index by index.)
Step 2: Create two new lists (before and after) with the same number of items as a and b to represent the result of the comparison function. The values are any null or non-null value. Something like:
before = [2, 3]
after = [null, null]
(2 should come before 1, representing 1 from b as null. The non-null values are preserved, but any non-null value could be in 2's place.)
I can impose a limitation that the values in before and after must be items from the lists a and b, but I'd prefer not to if I possibly can. I mention it because I'm unsure how I could know where the non-null value came from (a or b). But if the items compared from a and b are the same but only one is null at the end, I have the same problem.
Step 3: Use those two lists before and after to sort the original list. Something like:
sort([1, 2, 3], greaterThan) => [3, 2, 1]
// a = [2, 3]
// b = [1, 2]
// before = [2, 3]
// after = [null, null]
(If both values are non-null or both null, it should favor their original order relative to each other, or a "stable" sort.)
In such a trivial example, the items in a and b are sufficient to sort the list. The Javascript (the language the interpreter is written in) Array.sort method will compare them like this:
(2, 1)
(3, 2)
and be done. But if the order of the original list were [2, 3, 1] then it has to do:
(3, 2)
(1, 3)
(1, 2)
(1, 3)
(I don't know why or what algorithm they use).
In that example, I would have to provide lists a and b as [3, 1, 1, 1] and [2, 3, 2, 3] respectively.
How do I get a list for a and b that will work given any comparison function or order of the original list -- and then use the resulting before and after lists to sort that original list?

split the contents of the array into two other arrays

Simple example but I want to understand how it is done so I can apply it else where I have a main array with 6 elements. I want to take 3 of the elements from the main array and put it in a array and then take the other 3 from main array and put them in b array. I will use this to apply it to dealing cards to two players
main = [1, 2, 3, 4, 5, 6]
a = [ ]
b = [ ]
main = [1, 2, 3, 4, 5, 6]
#=> [1, 2, 3, 4, 5, 6]
main.first(3)
#=> [1, 2, 3]
main.last(3)
#=> [4, 5, 6]
a = [1, 2, 3, 4, 5, 6]
#=> [1, 2, 3, 4, 5, 6]
b = a.take(3)
#=> [1, 2, 3]
c = a.drop(3)
#=> [4, 5, 6]
All may have given the right answer, But as I understood from your question (I will use this to apply it to dealing cards to two players) When you dealing cards, as you deal cards to player main array should remove that element from self array to overcome Redundancy Problem (duplication). When you deal the all cards main array must be empty.
For this solution have a look at Array#shift
> main = [1,2,3,4,5,6] # I have 6 cards on my hand before dealing cards to players
=> [1, 2, 3, 4, 5, 6]
> a = main.shift(3) # given 3 cards to Player a
=> [1, 2, 3]
> b = main.shift(3) # given 3 cards to Player b
=> [4, 5, 6]
> main # after dealing all cards to two players I should not have any card on my hand
=> []
You have many ways to do the same thing in Ruby. Splitting arrays isn't an exception. Many answers (and comments) told you some of the ways to do that. If your program is dealing cards, you won't stop there. First, you'll probably have more than 6 cards. Second, you're probably going to have more than 2 players. Let's say the cards are C and the players are P. You need to write a method that, no matter how many Cs or Ps there are, the method is going to give each Player an equal number of Ccards (or return an error if it can't give it an equal number of cards). So for 6 cards and 2 players, it will give 3 cards each. For 12 cards and 3 players, 4 cards each. For 3 cards and 2 players, it's going to produce an error because the cards can't be evenly split:
def split_cards_evenly_between_players(cards, players)
if cards.size % players != 0
raise 'Cannot split evenly!'
else
groups_to_split_into = cards.size / players
cards.each_slice(groups_to_split_into).to_a
end
end
Let's go through the code. If the cards can't be evenly split between players, then the remainder by dividing them won't be 0 (6 cards / 3 players = remainder 0. 7 cards / 3 players = remainder 1). That's what line 2 checks. If the cards CAN be split, then we first find the groups to split into (which is dividing the number of cards by the number of players). Then we just split the array into that many groups with Enumerable#each_slice. Finally, since this doesn't produce an array, we need .to_a to convert it. The return value in Ruby is always the value of the last expression executed. The only expression in this method is the if/then expression which also returns the value of the last expression executed (which is the line where each_slice is). Let's try it out:
p split_cards_evenly_between_players([1,2,3,4,5,6,7,8,9,10,11,12],2) #=> [[1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12]]
p split_cards_evenly_between_players([4,5,1,2,5,3], 3) #=> [[4, 5], [1, 2], [5, 3]]
p split_cards_evenly_between_players([1,2,3],2) #=> Error: Cannot split evenly!
The nice thing about Ruby is its simple syntax and the fact it tries to get out of your way while solving a problem so you can focus more on the actual problem than the code.

Combine lists to the least possible amount of 2-dimensional lists

Sorry for the bad description in the title.
Consider a 2-dimensional list such as this:
list = [
[1, 2],
[2, 3],
[3, 4]
]
If I were to extract all possible "vertical" combinations of this list, for a total of 2*2*2=8 combinations, they would be the following sequences:
1, 2, 3
2, 2, 3
1, 3, 3
2, 3, 3
1, 2, 4
2, 2, 4
1, 3, 4
2, 3, 4
Now, let's say I remove some of these sequences. Let's say I only want to keep sequences which have either the number 2 in position #1 OR number 4 in position #3. Then I would be left with these sequences:
2, 2, 3
2, 3, 3
1, 2, 4
2, 2, 4
1, 3, 4
2, 3, 4
The problem
I would like to re-combine these remaining sequences to the least possible amount of 2-dimensional lists needed to contain all sequences but no less or no more.
By doing so, the resulting 2-dimensional lists in this particular example would be:
list_1 = [
[2],
[2, 3],
[3, 4]
]
list_2 = [
[1],
[2, 3],
[4]
]
In this particular case, the resulting lists can be thought out. But how would I go about if there were thousands of sequences yielding hundereds of 2-dimensional lists? I have been trying to come up with a good algorithm for two weeks now, but I am getting nowhere near a satisfying result.
Divide et impera, or divide and conquer. If we have a logical expression, stating that the value at position x should be a or the value at position y should be b, then we have 3 cases:
a is the value at position x and b is the value at position y
a is the value at position x and b is not the value at position y
a is not the value at position x and b is the value at position y
So, first you generate all your scenarios, you know now that you have 3 scenarios.
Then, you effectively separate your cases and handle all of them in a sub-routine as they were your main tasks. The philosophy behind divide et imera is to reduce your complex problem into several similar, but less complex problems, until you reach triviality.

Sorting arrays, and selecting lowest numbers?

I have created several arrays, containing multiple integers. Now i want the integers to be sorted, lowest first. Say for instance, i have this in an array: 6,6,1,2,4,4, i want it to be sorted: 1,2,4,4,6,6. Also, is there anyway i can make ruby recognize the 4 lowest values, and display them somehow? I have tried to mess around with .show, but since im quite new to programming i'm rather confused by the results i receive.
did you try this?
a = [6,6,1,2,4,4]
p a.sort
#=> [1, 2, 4, 4, 6, 6]
sort will sort in ascending order.
if you need them sorted in descending order, use sort with a block:
p a.sort {|a,b| b <=> a}
#=> [6, 6, 4, 4, 2, 1]
UPDATE: not sure how i missed the part about lowest values ...
thank you #Mladen
a.sort.take(4)
#=> [1, 2, 4, 4]

Loop through different sets of unique permutations

I'm having a hard time getting started to layout code for this problem.
I have a fixed amount of random numbers, in this case 8 numbers.
R[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
That are going to be placed in 3 sets of numbers, with the only constraint that each set contain minimum one value, and each value can only be used once. Edit: all 8 numbers should be used
For example:
R1[] = { 1, 4 }
R2[] = { 2, 8, 5, 6 }
R3[] = { 7, 3 }
I need to loop through all possible combinations of a set R1, R2, R3. Order is not important, so if the above example happened, I don't need
R1[] = { 4, 1 }
R2[] = { 2, 8, 5, 6 }
R3[] = { 7, 3 }
NOR
R1[] = { 2, 8, 5, 6 }
R2[] = { 7, 3 }
R3[] = { 1, 4 }
What is a good method?
I have in front of me Knuth Volume 4, Fascicle 3, Generating all Combinations and Partitions, section 7.2.1.5 Generating all set partitions (page 61 in fascicle).
First he details Algorithm H, Restricted growth strings in lexicographic order due to George Hutchinson. It looks simple, but I'm not going to dive into it just now.
On the next page under an elaboration Gray codes for set partitions he ponders:
Suppose, however, that we aren't interested in all of the partitions; we might want only the ones that have m blocks. Can we run this through the smaller collection of restricted growth strings, still changing one digit at a time?
Then he details a solution due to Frank Ruskey.
The simple solution (and certain to be correct) is to code Algorithm H filtering on partitions where m==3 and none of the partitions are the empty set (according to your stated constraints). I suspect Algorithm H runs blazingly fast, so the filtering cost will not be large.
If you're implementing this on an 8051, you might start with the Ruskey algorithm and then only filter on partitions containing the empty set.
If you're implementing this on something smaller than an 8051 and milliseconds matter, you can seed each of the three partitions with a unique element (a simple nested loop of three levels), and then augment by partitioning on the remaining five elements for m==3 using the Ruskey algorithm. You won't have to filter anything, but you do have to keep track of which five elements remain to partition.
The nice thing about filtering down from the general algorithm is that you don't have to verify any cleverness of your own, and you change your mind later about your constraints without having to revise your cleverness.
I might even work a solution later, but that's all for now.
P.S. for the Java guppies: I discovered searching on "George Hutchison restricted growth strings" a certain package ca.ubc.cs.kisynski.bell with documentation for method growthStrings() which implements the Hutchison algorithm.
Appears to be available at http://www.cs.ubc.ca/~kisynski/code/bell/
Probably not the best approach but it should work.
Determine number of combinations of three numbers which sum to 8:
1,1,6
1,2,5
1,3,4
2,2,4
2,3,3
To find the above I started with:
6,1,1 then subtracted 1 from six and added it to the next column...
5,2,1 then subtracted 1 from second column and added to next column...
5,1,2 then started again at first column...
4,2,2 carry again from second to third
4,1,3 again from first...
3,2,3 second -> third
3,1,4
knowing that less than half is 2 all combinations must have been found... but since the list isn't long we might as well go to the end.
Now sort each list of 3 from greatest to least(or vice versa)
Now sort each list of 3 relative to each other.
Copy each unique list into a list of unique lists.
We now have all the combinations which add to 8 (five lists I think).
Now consider a list in the above set
6,1,1 all the possible combinations are found by:
8 pick 6, (since we picked six there is only 2 left to pick from) 2 pick 1, 1 pick 1
which works out to 28*2*1 = 56, it is worth knowing how many possibilities there are so you can test.
n choose r (pick r elements from n total options)
n C r = n! / [(n-r)! r!]
So now you have the total number of iterations for each component of the list for the first one it is 28...
Well picking 6 items from 8 is the same as creating a list of 8 minus 2 elements, but which two elements?
Well if we remove 1,2 that leaves us with 3,4,5,6,7,8. Lets consider all groups of 2... Starting with 1,2 the next would be 1,3... so the following is read column by column.
12
13 23
14 24 34
15 25 35 45
16 26 36 46 56
17 27 37 47 57 67
18 28 38 48 58 68 78
Summing each of the above columns gives us 28. (so this only covered the first digit in the list (6,1,1) repeat the procedure for the second digit (a one) which is "2 Choose 1" So of the left over two digits from the above list we pick one of two and then for the last we pick the remaining one.
I know this is not a detailed algorithm but I hope you'll be able to get started.
Turn the problem on it's head and you'll find a straight-forward solution. You've got 8 numbers that each need to be assigned to exactly one group; The "solution" is only a solution if at least one number got assigned to each group.
The trivial implementation would involve 8 for loops and a few IF's (pseudocode):
for num1 in [1,2,3]
for num2 in [1,2,3]
for num3 in [1,2,3]
...
if ((num1==1) or (num2==1) or (num3 == 1) ... (num8 == 1)) and ((num1 == 2) or ... or (num8 == 2)) and ((num1 == 3) or ... or (num8 == 3))
Print Solution!
It may also be implemented recursively, using two arrays and a couple of functions. Much nicer and easier to debug/follow (pseudocode):
numbers = [1, 2, 3, 4, 5, 6, 7, 8]
positions = [0, 0, 0, 0, 0, 0, 0, 0]
function HandleNumber(i) {
for position in [1,2,3] {
positions[i] = position;
if (i == LastPosition) {
// Check if valid solution (it's valid if we got numbers in all groups)
// and print solution!
}
else HandleNumber(i+1)
}
}
The third implementation would use no recursion and a little bit of backtracking. Pseudocode, again:
numbers = [1,2,3,4,5,6,7,8]
groups = [0,0,0,0,0,0,0,0]
c_pos = 0 // Current position in Numbers array; We're done when we reach -1
while (cpos != -1) {
if (groups[c_pos] == 3) {
// Back-track
groups[c_pos]=0;
c_pos=c_pos-1
}
else {
// Try the next group
groups[c_pos] = groups[c_pos] + 1
// Advance to next position OR print solution
if (c_pos == LastPostion) {
// Check for valid solution (all groups are used) and print solution!
}
else
c_pos = c_pos + 1
}
}
Generate all combinations of subsets recursively in the classic way. When you reach the point where the number of remaining elements equals the number of empty subsets, then restrict yourself to the empty subsets only.
Here's a Python implementation:
def combinations(source, n):
def combinations_helper(source, subsets, p=0, nonempty=0):
if p == len(source):
yield subsets[:]
elif len(source) - p == len(subsets) - nonempty:
empty = [subset for subset in subsets if not subset]
for subset in empty:
subset.append(source[p])
for combination in combinations_helper(source, subsets, p+1, nonempty+1):
yield combination
subset.pop()
else:
for subset in subsets:
newfilled = not subset
subset.append(source[p])
for combination in combinations_helper(source, subsets, p+1, nonempty+newfilled):
yield combination
subset.pop()
assert len(source) >= n, "Not enough items"
subsets = [[] for _ in xrange(n)]
for combination in combinations_helper(source, subsets):
yield combination
And a test:
>>> for combination in combinations(range(1, 5), 2):
... print ', '.join(map(str, combination))
...
[1, 2, 3], [4]
[1, 2, 4], [3]
[1, 2], [3, 4]
[1, 3, 4], [2]
[1, 3], [2, 4]
[1, 4], [2, 3]
[1], [2, 3, 4]
[2, 3, 4], [1]
[2, 3], [1, 4]
[2, 4], [1, 3]
[2], [1, 3, 4]
[3, 4], [1, 2]
[3], [1, 2, 4]
[4], [1, 2, 3]
>>> len(list(combinations(range(1, 9), 3)))
5796

Resources