Related
I'd like to detect a "complete loop". Let's suppose that lists are connectable if the first value of a list is equal to the end value of another list. For example, we may have 5 lists like below.
a1 = [1, 14, 0]
a2 = [2, 14, 3]
a3 = [0, 14, 2]
a4 = [3, 14, 1]
a5 = [0, 14, 3]
where a1, a3, a2, and a4 can be connected to each other, forming a "complete loop" as a4 and a1 can also be connected. So the output shall be [a1, a3, a2, a4].
(It looked like as if it were an NP-Hard Problem, but I think I was wrong!) I've tried solutions such as Doubly Linked List or Hash Table, but it somehow then turned out to be recursive programming, which I'm terrible at and was not very successful.
Of course, I may just generate all the permutations and check one by one. Can there be a more beautiful way?
This is not NP-hard. Conceptually, this problem is known as 'cycle detection' and is a part of graph theory.
If you would like to see the code approaches to cycle detection, here is a decent beginner's tutorial:
https://www.tutorialspoint.com/Detect-Cycle-in-a-an-Undirected-Graph
https://www.techiedelight.com/check-undirected-graph-contains-cycle-not
If this problem was NP-hard, we wouldn't be able to do a lot of relatively-important things like dependency calculations and circular reference detection.
Given the following array:
y = %w[A1 A2 B5 B12 A6 A8 B10 B3 B4 B8]
=> ["A1", "A2", "B5", "B12", "A6", "A8", "B10", "B3", "B4", "B8"]
With the expected sorted array to be:
=> ["A1", "A2", "A6", "A8", "B3", "B4", "B5", "B8", "B10", "B12"]
Using the following (vanilla) sort, I get:
irb(main):2557:0> y.sort{|a,b| puts "%s <=> %s = %s\n" % [a, b, a <=> b]; a <=> b}
A1 <=> A8 = -1
A8 <=> B8 = -1
A2 <=> A8 = -1
B5 <=> A8 = 1
B4 <=> A8 = 1
B3 <=> A8 = 1
B10 <=> A8 = 1
B12 <=> A8 = 1
A6 <=> A8 = -1
A1 <=> A2 = -1
A2 <=> A6 = -1
B12 <=> B3 = -1
B3 <=> B8 = -1
B5 <=> B3 = 1
B4 <=> B3 = 1
B10 <=> B3 = -1 # this appears to be wrong, looks like 1 is being compared, not 10.
B12 <=> B10 = 1
B5 <=> B4 = 1
B4 <=> B8 = -1
B5 <=> B8 = -1
=> ["A1", "A2", "A6", "A8", "B10", "B12", "B3", "B4", "B5", "B8"]
...which is obviously not what I desire. I know I can attempt to split on the alpha first and then sort the numerical, but it just seems like I shouldn't have to do that.
Possible big caveat: we're stuck using Ruby 1.8.7 for now :( But even Ruby 2.0.0 is doing the same thing. What am I missing here?
Suggestions?
You are sorting strings. Strings are sorted like strings, not like numbers. If you want to sort like numbers, then you should sort numbers, not strings. The string 'B10' is lexicographically smaller than the string 'B3', that's not something unique to Ruby, that's not even something unique to programming, that's how lexicographically sorting a piece of text works pretty much everywhere, in programming, databases, lexicons, dictionaries, phonebooks, etc.
You should split your strings into their numerical and non-numerical components, and convert the numerical components to numbers. Array sorting is lexicographic, so this will end up sorting exactly right:
y.sort_by {|s| # use `sort_by` for a keyed sort, not `sort`
s.
split(/(\d+)/). # split numeric parts from non-numeric
map {|s| # the below parses numeric parts as decimals, ignores the rest
begin Integer(s, 10); rescue ArgumentError; s end }}
#=> ["A1", "A2", "A6", "A8", "B3", "B4", "B5", "B8", "B10", "B12"]
Here are a couple of ways to do that.
arr = ["A1", "A2", "B5", "B12", "A6", "AB12", "A8", "B10", "B3", "B4",
"B8", "AB2"]
Sort on a 2-element array
arr.sort_by { |s| [s[/\D+/], s[/\d+/].to_i] }
#=> ["A1", "A2", "A6", "A8", "AB2", "AB12", "B3", "B4", "B5", "B8",
# "B10", "B12"]
This is similar to #Jorg's solution except I've computed the two elements of the comparison array separately, rather than splitting the string into two parts and converting the latter to an integer.
Enumerable#sort_by compares each pair of elements of arr with the spaceship method, <=>. As the elements being compared are arrays, the method Array#<=> is used. See in particular the third paragraph of that doc.
sort_by compares the following 2-element arrays:
arr.each { |s| puts "%s-> [%s, %d]" %
["\"#{s}\"".ljust(7), "\"#{s[/\D+/]}\"".ljust(4), s[/\d+/].to_i] }
"A1" -> ["A" , 1]
"A2" -> ["A" , 2]
"B5" -> ["B" , 5]
"B12" -> ["B" , 12]
"A6" -> ["A" , 6]
"AB12" -> ["AB", 12]
"A8" -> ["A" , 8]
"B10" -> ["B" , 10]
"B3" -> ["B" , 3]
"B4" -> ["B" , 4]
"B8" -> ["B" , 8]
"AB2" -> ["AB", 2]
Insert spaces between the alphameric and numeric parts of the string
max_len = arr.max_by(&:size).size
#=> 4
arr.sort_by { |s| "%s%s%d" % [s[/\D+/], " "*(max_len-s.size), s[/\d+/].to_i] }
#=> ["A1", "A2", "A6", "A8", "AB2", "AB12", "B3", "B4", "B5", "B8",
# "B10", "B12"]
Here sort_by compares the following strings:
arr.each { |s| puts "%s-> \"%s\"" %
["\"#{s}\"".ljust(7), s[/\D+/] + " "*(max_len-s.size) + s[/\d+/]] }
"A1" -> "A 1"
"A2" -> "A 2"
"B5" -> "B 5"
"B12" -> "B 12"
"A6" -> "A 6"
"AB12" -> "AB12"
"A8" -> "A 8"
"B10" -> "B 10"
"B3" -> "B 3"
"B4" -> "B 4"
"B8," -> "B 8"
"AB2" -> "AB 2"
If you know what the maximum amount of digits in your numbers is you can also prefix your numbers with 0 during comparison.
y.sort_by { |string| string.gsub(/\d+/) { |digits| format('%02d', digits.to_i) } }
#=> ["A1", "A2", "A6", "A8", "B3", "B4", "B5", "B8", "B10", "B12"]
Here '%02d' specifies the following, the % denotes the formatting of a value, the 0 then specifies to prefix the number with 0s, the 2 specifies the total length of the number, the d specifies that you want the output in decimals (base 10). You can find additional info here.
This means that 'A1' will be converted to 'A01', 'B8' will become 'B08' and 'B12' will stay 'B12', since it already has 2 digits. This is only used during comparison.
A natural or lexicographic sort, not a standard character-value-based sort, would be needed. Something like these gems would be a starting point: https://github.com/dogweather/naturally, https://github.com/johnnyshields/naturalsort
Humans treat a string like "A2" as "A" followed by the number 2, and sort by using character-string sorting for the string part and numeric sorting for the numeric part. Standard sort() uses character-value sorting treating the string as a sequence of characters regardless of what the characters are. So for sort() "A10" and "A2" look like [ 'A', '1', '0' ] and [ 'A', '2' ], since '1' sorts before '2' and the following characters can't change that order "A10" thus sorts before "A2". For humans the same strings look like [ "A", 10 ] and [ "A", 2 ], 10 sorts after 2 so we get the opposite result. The strings can be manipulated to make the character-value-based sort() produce the expected result by making the numeric portion fixed-width and zero-padding it on the left to avoid embedded spaces, making "A2" turn into "A02" which does sort before "A10" using standard sort().
Here is the questions, setof returns a set of solutions
solve([A1, A2, A3, B1, B3, C1, C2, C3, T]):-
permutation([1,2,3,4,5,6,7,8], [A1, A2, A3, B1, B3, C1, C2, C3]),
T is A1 + A2 + A3,
A1 + A2 + A3 =:= A1 + B1 + C1,
A1 + A2 + A3 =:= A3 + B3 + C3,
A1 + A2 + A3 =:= C1 + C2 + C3.
Then I find all solutions
?- setof(Z,solve([A1,A2,A3,B1,B3,C1,C2,C3,T]),Set).
A1 = 1,
A2 = 4,
A3 = 8,
B1 = 7,
B3 = 3,
C1 = 5,
C2 = 6,
C3 = 2,
T = 13,
Set = [_G5594] ;
A1 = 1,
A2 = 5,
A3 = 6,
B1 = 8,
B3 = 4,
C1 = 3,
C2 = 7,
C3 = 2,
T = 12,
Set = [_G5578]
...
How do I to filter and score results to find the solutions has max T value (15)?
I recommend to use CLP(FD) constraints for such combinatorial tasks.
For example:
:- use_module(library(clpfd)).
solution([A1,A2,A3,B1,B3,C1,C2,C3], T):-
[A1,A2,A3,B1,B3,C1,C2,C3] ins 1..8,
T #= A1 + A2 + A3,
A1 + A2 + A3 #= A1 + B1 + C1,
A1 + A2 + A3 #= A3 + B3 + C3,
A1 + A2 + A3 #= C1 + C2 + C3.
The exact details vary a bit between Prolog systems, so check your system's manual for more information. The maximum can be obtained via labeling options, such as:
?- solution(Vs, T), labeling([max(T)], Vs).
Vs = [8, 8, 8, 8, 8, 8, 8, 8],
T = 24 ;
Vs = [7, 8, 8, 8, 7, 8, 7, 8],
T = 23 ;
Vs = [7, 8, 8, 8, 8, 8, 8, 7],
T = 23 ;
etc.
See clpfd for more information.
You can commit to the first solution with once/1.
I'd like to know whether two Ruby arrays have the same elements, though not necessarily in the same order. Is there a native way to do this? The equality operators for Array seem to check whether the items are the same and the order is the same, and I need to relax the latter condition.
This would be extremely easy to write, I just wonder if there's a native idiom.
If you don't have duplicate items either, you could use Set instead of Array:
Set implements a collection of unordered values with no duplicates.
This is a hybrid of Array's intuitive inter-operation facilities and
Hash's fast lookup.
Example:
require 'set'
s1 = Set.new [1, 2, 3] # -> #<Set: {1, 2, 3}>
s2 = [3, 2, 1].to_set # -> #<Set: {3, 2, 1}>
s1 == s2 # -> true
[2,1].uniq.sort == [1,2].uniq.sort #=> true
[2,1,4].uniq.sort == [1,2].uniq.sort #=> false
or
a1 = [1,2,3]
a2 = [2,3,1]
p (a2-a1).empty? && (a1-a2).empty? #=> true
a1 = [1,2,3]
a2 = [4,3,1]
p (a2-a1).empty? && (a1-a2).empty? #=> false
a1 = [1,2,3]
a2 = [2,3,1,5]
p (a2-a1).empty? && (a1-a2).empty? #=> false
This would be extremely easy to write, I just wonder if there's a native idiom.
I'm afraid there's no native idiom for it.
If your arrays contains multiple values that you want to count on both arrays you'll have to use #sort to put them in the same order. Once you have done that you can easily compare them:
a.sort == b.sort
Otherwise you can use #uniq that will extract the unique values of the arrays (to make it faster) and use #sort like above:
a.uniq.sort == b.uniq.sort
a1 = [1, 2, 3, 4]
a2 = [4, 2, 1, 3]
(a1 & a2).size == a1.size # => true
a3 = [1, 2, 3, 5]
(a1 & a3).size == a1.size # => false
A[] -> 1 3 5 7 2 4 6 8 //
lb=0,mid-1=3,mid+1=4,ub=7;
a=3,b=7,ab=7;
1st iteration
a=3,b=6,ab=6;
2nd iteration
swap(A[ab],A[a]) // int t; t i'll using for temporary storage
1 3 5 6 2 4 7 8
b=5,ab=5;
sort(A,lb,mid-1); // using bubble sort
3rd iteration
swap(A[ab],A[a])
1 3 5 4 2 6 7 8
b=5,ab=4
sort(A,lb,mid-1) // using bubble sort
Is this correct approach for Merge sort using inplace merging. This is my first attempt about inplace merging.If it is not correct approach someone can suggest me.
Not sure that you have Mergesort algorithm.
Using Mergesort at first phase you need to split you array into subarrays.
A = [1, 3, 5, 7, 2, 4, 6, 8]
A1 = [1, 3, 5, 7], A2 = [2, 4, 6, 8]
A11 = [1,3], A12 = [5,7], A21 = [2,4], A22 = [6,8]
... // till you have an arrays looks like this:
A1 = [1], A2 = [3], A3 = [5], A4 = [7], A5 = [2], A6 = [4], A7 = [6], A8 = [8]
Then you merging in reverse order, and compare only first elements in both arrays (put lowest element in new array).
[1,3], [5,7], [2,4], [6,8]
[1,3,5,7], [2,4,6,8]
[1,2,3,4,5,6,7,8]