So... I have the following:
A class with several properties which are retrieved from an .xml file. These properties are if the object is a condition (it has two children) and its name. Basically, the object's children properties are the names of its children.
The .xml looks like this:
<object-2>
<name>Object - 2</name>
<yesChild>Object - 3</yesChild>
<noChild>Object - 4</noChild>
</object-2>
If the noChild is empty, then it means that the object is not a condition. All objects retrieved from the .xml are stored into an array.
What I need is to somehow create a tree out of it and identify all paths that can be taken in order to reach the last element in the array. The algorithm does not need to traverse all nodes, just the ones it needs to reach the last element of the array.
Example:
We have 4 objects: X1, X2, X3 and X4, where X1 is a condition with X2 and X3 as its children then we will have 2 paths that start in X1 and end in X4.
Path 1: X1->X2->X4
Path 2: X1->X3->X4
Thank you.
Since you don't show what format the data is after you parse, I'm going to guess :) Here's how I would store the parsed data in ruby objects (using new-style hash key syntax for clarity):
[ {yes: 2, no: 3},
{yes: 4},
{yes: 4},
{yes: -1} ]
Then, tree-traversal can be done recursively. As long as your arrays aren't several thousands of elements long, this will work fine.
def tree(object_number, list)
if object_number == list.size
[[object_number]]
else
list[object_number-1].values.map { |obj_num|
tree(obj_num,list)
}.inject{|a,b| a+b}.map{|l| [object_number] + l}
end
end
Now you call the function:
tree(1,data)
=> [[1, 2, 4], [1, 3, 4]]
data = [ {yes: 2, no: 3}, {yes: 4, no:5}, {yes:5, no:4}, {yes:5}, {yes: -1} ]
tree(1,data)
=> [[1, 2, 4, 5], [1, 2, 5], [1, 3, 5], [1, 3, 4, 5]]
How it works: The easiest way to build this list is backwards, since we only know the number of paths once we have gotten to the end of all of them. So this code follows the references all the way to the end, and when it gets to the last object, it returns it as a single-element two dimensional array.
tree(5,list)
=> [[5]]
at each level of recursion, it takes the results of it's recursion call (returned as a list of lists) and prepends it's own object number to each of the interior lists. So, following back up the tree:
tree(4,list) # prepends 4 to tree(5)
=> [[4,5]]
tree(3,list) # prepends 3 to tree(4) and tree(5)
=> [[3,4,5],[3,5]]
tree(2,list) # prepends 2 to tree(4) and tree(5)
=> [[2,4,5],[2,5]]
tree(1,list) # prepends 1 to tree(2) and tree(3)
=> [[1, 2, 4, 5], [1, 2, 5], [1, 3, 5], [1, 3, 4, 5]]
If the lists might be long enough to overflow your stack, it is always possible to do this without recursion. Recursion is just the simplest way for this particular problem.
Related
This question already has answers here:
What does map(&:name) mean in Ruby?
(17 answers)
Closed 2 years ago.
I done a coding challenge in Ruby not too long ago and wanted a better understanding of how the syntax below works, particularly with the last part of the expression (&:first).
def remove_every_other(arr)
arr.each_slice(2).map(&:first)
end
For some background the task was to take an array and remove every second element from the array.
Tests:
Test.assert_equals(remove_every_other(['Hello', 'Goodbye', 'Hello Again']), #=> ['Hello', 'Hello Again'])
Test.assert_equals(remove_every_other([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), #=> [1, 3, 5, 7, 9])
Test.assert_equals(remove_every_other([[1, 2]]), #=> [[1, 2]])
Test.assert_equals(remove_every_other([['Goodbye'], {'Great': 'Job'}]), #=> [['Goodbye']])
Test.assert_equals(remove_every_other([]), [])
& is a reference to a method.
:first is a symbol.
So &:first is simply a reference to the method named first in any object.
So this is the same thing as saying arr.each_slice(2).map {|it| it.first }.
When you map a method to elements of a collection, Ruby will simply call that method on the elements of the collection. In your case, arr.each_slice(2) will return elements of arr two by two (see the doc for each_slice), so if your array is e.g. [1, 2, 3, 4, 5, 6, 7, 8] it is a collection containing [[1, 2], [3, 4], [5, 6], [7, 8]]. Mapping :first on that collection means calling the first method on each element, and [1, 2].first simply returns 1 while [7, 8].first returns 7.
As the map method returns a collection with each element replaced with the result of the call to the method, you'll find yourself with a collection containing the first of each pair. This is how it removes every other element.
Note that if the original collection has an odd number of elements then the last one will be an array of one instead of two (see doc for each_slice), so if the array is [1, 2, 3] the slices are [[1, 2], [3]] and calling .first on each will result in [1, 3].
The task says: the numbers are represented by lists (e.g. 123 = [1, 2, 3]), write a predicate which adds two such numbers. (e.g. sum([4, 5, 6], [9], [4, 6, 5])).
I have been thinking how to express this recursively but what trips me up is when the sizes of the arrays vary. It seems, that this would be simple if the arrays were reverted, so that HEAD is actually the last element. Because the problem for me is that my sum looks like that that:
[4, 5, 6]
+
[9]
instead of
[4, 5, 6]
+
[9]
What could be the proper way to write such predicate? I need some pointers or reference for help...
Here is my implementation:
sum(L1,L2,OutL):-
reverse(L1,List1),reverse(L2,List2),
add_lists(List1,List2,0,List3),
reverse(List3,OutL).
add_lists([],[],0,[]).
add_lists([],[],1,[1]).
add_lists([],[H|T],C,[H1|T]):-H1 is H+C.
add_lists([H|T],[],C,[H1|T]):-H1 is H+C.
add_lists([H|T],[H1|T1],C,[H2|T2]):-NH is H1+H,
(NH > 10 -> NC is 1,H2 is NH+C-10; H2 is NH+C,NC is 0),
add_lists(T,T1,NC,T2).
The idea is to reverse lists in order to add right positions and avoid the problem you referred. Also you just keep a carry which is 1 if an addition is greater than 10 where you reduce 10.
Example:
?- sum([4, 5, 6], [9], L).
L = [4, 6, 5] ;
false.
Suppose I have the a tree given in the nested list representation, how do I traverse it breadth first? For example, if I'm given
[1, [2, [3, [4, [3, 5]]]], [3, [4, 5, 2]]]
The output would be
[1,2,3,3,4,4,5,2,3,5]
Also, given a flattened representation of the depth-first order like [1,2,3,4,3,5,3,4,5,2], how do I find the indices of the breadth-first order?
Thanks in advance for any help.
Here's the code in Python:
queue = [1, [2, [3, [4, [3, 5]]]], [3, [4, 5, 2]]]
while queue:
firstItem = queue.pop(0)
if type(firstItem) is list:
for item in firstItem:
queue.append(item)
else:
print('Traversed %d' % (firstItem))
The output is:
Traversed 1
Traversed 2
Traversed 3
Traversed 3
Traversed 4
Traversed 5
Traversed 2
Traversed 4
Traversed 3
Traversed 5
After studying my output and what you specified the output should be in your question, I think my output is more correct. More specifically, the left most 3 in the input list and [4, 5, 2] at the end of the input list are on the same "level", and thus should be traversed 3, 4, 5, 2, as shown from the 4th line to the 7th line of my output.
As for your second question, I think you should ask a separate question because it really is a completely different question.
This question already has answers here:
What does the (unary) * operator do in this Ruby code?
(3 answers)
What does the * (star) mean in Ruby? [duplicate]
(1 answer)
Closed 7 years ago.
I read this code that is about quicksort with monkey-patching for the Array class.
class Array
def quicksort
return [] if empty?
pivot = delete_at(rand(size))
left, right = partition(&pivot.method(:>))
return *left.quicksort, pivot, *right.quicksort
end
end
I don't know what the star (*) sign seen at the start of *left.quicksort is. Can't we just use left.quicksort?
The star (in this case) stands for array unpacking. The idea behind it is that you want to get one array with the given elements, instead of array of array, element, array:
left = [1, 2, 3]
pivot = 4
right = [5, 6, 7]
[left, pivot, right] # => [[1, 2, 3], 4, [5, 6, 7]]
[*left, pivot, *right] # => [1, 2, 3, 4, 5, 6, 7]
The * takes a list of arguments and splits them into individual elements.
This allows you to return one un-nested array even when the left and right themselves return an array.
Regarding can't we just use left.quicksort did you give it a try?
def a()
return *[1,2,3], 4, *[5,6]
end
def b()
return [1,2,3], 4, *[5,6]
end
b()
=> [[1, 2, 3], 4, 5, 6]
a()
=> [1, 2, 3, 4, 5, 6]
Without the asterisk, you will have three values returned... the first and last value will be a single array with multiple values
[array1], pivot, [array2]
With the asterisk, the array values will be returned as separate components...
array1_value_1, array1_value_2, array1_value_3, ..., pivot, array2_value_1, array2_value2, array2_value_3, ...
Let's say I have a class of 30 students and want generate every possible way in which they can be partitioned into groups of 5 (order is irrelevant).
I know how to find all the combinations of students to form one group individually (http://www.merriampark.com/comb.htm). By using that iterator and some recursion, I can find PERMUTATIONS of the possible group combinations. However, order in which the groups are selected isn't relevant and I'd like to minimize my execution time. So how do I find the unique COMBINATIONS of the possible groups?
The above algorithm uses lexicographical ordering to avoid generating duplicate combinations... is there a way that I can use that idea on groups instead of on objects?
I know Ruby well and Java/Python less well. Thanks in advance for any advice!
Well, there's (30C5*25C5*20C5*15C5*10C5*5C5)/6! = 30!/(6!*5!6) = 123,378,675,083,039,376 different partitons of 30 into groups of 5, so generating them all will take some time, no matter what method you use.
In general, though, a good method to selecting such a partition is to use some ordering on the elements, and find the grouping for the highest ungrouped element, and then group the rest.
find_partition = lambda do |elts|
if elts.empty?
[[]]
else
highest = elts.pop
elts.combination(4).map do |others|
find_partition[elts - others].map { |part| part << [highest,*others] }
end.inject(:+)
end
end
find_partition[(1..30).to_a]
This way you're only generating each partition once
This is an old question, but anyway, for the record, that's how I would it in Ruby:
class Array
def groups_of_size(n)
Enumerator.new do |yielder|
if self.empty?
yielder.yield([])
else
self.drop(1).combination(n-1).map { |vs| [self.first] + vs }.each do |values|
(self - values).groups_of_size(n).each do |group|
yielder.yield([values] + group)
end
end
end
end
end
end
I use an enumerator because the output can grow very quickly, a strict output (an array for example) wouldn't be useful. A usage example:
>> pp [0, 1, 2, 3, 4, 5].groups_of_size(3).to_a
=>
[[[0, 1, 2], [3, 4, 5]],
[[0, 1, 3], [2, 4, 5]],
[[0, 1, 4], [2, 3, 5]],
[[0, 1, 5], [2, 3, 4]],
[[0, 2, 3], [1, 4, 5]],
[[0, 2, 4], [1, 3, 5]],
[[0, 2, 5], [1, 3, 4]],
[[0, 3, 4], [1, 2, 5]],
[[0, 3, 5], [1, 2, 4]],
[[0, 4, 5], [1, 2, 3]]]
You could do some post-processing on the permutations. Some pseudo-code (implement in the language of your choice...):
// We have a list of lists called 'permutations'
// combinations is an (empty) list of lists
for each permutation in permutations
{
sortedPermutation = permutation.sort()
if (! combinations.find(sortedPermutation) )
{
combinations.add(sortedPermutation);
}
}
Probably not the most efficient; I'd add the sort & compare to the code that generates the permutations personally.
One possibility would be to find all combinations to form an individual group, then go through and generate combinations that don't contain members of that individual group. Something like:
List<List<Student>> combinations=Combinations(students);
public void GenerateCombinations(int startingIndex, List<List<Student>> currentGroups, int groupsLeft)
{
if(groupsLeft==0) ProcessCombination(currentGroups);
for(int i=startingIndex; i<combinations.Count; i++)
{
if combinations[i] does not contain a student in current groups
GenerateCombinations(i+1, currentGroups + combinations[i], groupsLeft -1);
}
}
It won't be the most efficient method to go about it, but it should generate all combinations of groups. I suspect better performance could be had if you were to generate temporary lists of combinations, where in all groups that can't occur were removed, but that would be a bit more complex.
As a slight aside, there should be 142,506 combinations of 30 students to form a single group of 5. My <sarcasm> awesome </sarcasm> math skills suggest that there should be about 10^17 = 100 quadrillion combinations of groups of students (30!/((5!^6)*6!); 30! orderings of students, ordering of 6 groups of 5 does not matter, and ordering of those 6 groups doesn't matter). You might be sitting there a while waiting for this to finish.