Generator of graphs with known chromatic number - ruby

I have implemented several graph coloring algorithms as a class project and I would like to test and compare their performance. For this I need test graphs with known chromatic number.
I have expected this to be quite common requirement so that there will be algorithm and/or library readily available. It turned out that I was wrong. After quite extensive search the best I found is a paper from 1979.¹ In section 6 there is a method for generation of graphs with known chromatic number described. Before endeavoring to translate it from math to programmers I would like to know whether there really isn’t implementation available to avoid reinventing a wheel.
¹ Leighton, F. T. (1979). A graph coloring algorithm for large scheduling problems. In Journal of Research of the National Bureau of Standards (pp. 489-506). http://doi.org/10.6028/jres.084.024

So I don't know ruby, but came across this question when I was looking at the same paper and also confused. I've implemented the algorithm in Rust (link to playground) using the petgraph library to represent the graphs.
But the basic idea is that you want to choose some values:
a: multiplier (also called factor)
c: increment
m: modulus
x[0]: some seed or starting value
That define a pseudo-random number generator (specifically a Linear congruential generator wikipedia) which is defined by the recursive relation:
x[i] = (x[i - 1] * a + c) % m
For some nice properties, we also need the parameters to meet the criteria (which Leighton mentions in the paper as the first step):
m much greater than n (the number of nodes)
gcd(m, n) == k
gcd(m, c) == 1 or m and c are coprime
for any prime number p: if m % p == 0 then (a-1) % p == 0
if m % 4 == 0 then (a-1) % 4 == 0
I believe these criteria enable Leighton to prove that the procedure works, but I am not sure. Luckily, he provides some values for m, c, a, and x[0] in Appendix C. For example:
// Values taken from Leighton
let chromatic_number = 5; // k
let modulus = 84_035; // m
let increment = 6_859; // c
let multiplier = 8_401; // a
let mut x_i = vec![33_289]; // x[0]
After that, we generate a vector of random numbers x_i according to the recurrence relation:
let num_rand_numbers = 100;
for i in (1..num_rand_numbers) {
// x[i] = (x[i-1] * a + c) % m
x_i.push((x_i[i - 1] * multiplier + increment) % modulus);
}
So we've got random numbers x_i, but they're in the interval [0, m] and our nodes are in the range [0, n-1] where m is a lot greater than n. So create a vector y_i where:
// Construct y_i in [0, n-1] such that y_i = x_i % n
let y_i = x_i.iter() // Convert x_i to an iterator
.map(|x| x % num_nodes) // y_i = x_i % n
.collect::<Vec<_>>(); // convert iterator to vector
So there's a lot of work just to efficiently choose some random vectors but once they're chosen, the algorithm goes like:
Create a new undirected graph with num_nodes nodes. In my testing, Leighton's method resulted in lots of islands with some nodes completely unreachable from the others. So here I connect them all in a line so that we don't have any island nodes.
let mut graph = UnGraph::new_undirected();
let mut prev;
let mut curr = graph.add_node(Node {});
for n in 1..num_nodes {
prev = curr;
curr = graph.add_node(Node {});
graph.add_edge(prev, curr, Edge {});
}
Cache the node indices so we don't have to look them up every time
let node_indices = graph.node_indices().collect::<Vec<_>>();
Now define an h-clique as a set of nodes which are all fully connected to
each other, such that each node has h-1 edges coming from it.
This corresponds to h colours being required to colour that clique.
We create multiple cliques of decreasing degrees: start with the high-degree cliques and looping until you link up the cliques of degree 2.
let mut y_idx = 0;
// This for-loop takes the b-vector (which contains how many 2-cliques, 3-cliques, 4-cliques, ..., k-cliques there should be and converts it into multiple tuples like (number of cliques, size of those cliques)
'make_cliques: for (num_cliques, clique_degree) in b_vec.into_iter().zip((1..(k + 1)).into_iter()).rev() {
if clique_degree <= 1 {
break;
}
For each clique of size clique_degree that we want to create:
for clique_idx in 0..num_cliques {
// Create a clique with nodes indexed y_idx to y_idx+clique_degree
for i in 0..clique_degree {
for j in (i + 1)..clique_degree {
Go through the randomly-selected nodes, and attempt to connect them together
let a = node_indices[y_i[y_idx + i]];
let b = node_indices[y_i[y_idx + j]];
If we've already added this edge => don't add it again
if graph.contains_edge(a, b) {
continue;
}
Note that in my testing, Leighton's method would add edges to a node even if it already had the maximum number of edges. So here I check if either of the candidate nodes already have the maximum number of neighbours, and if it does, I don't add this edge.
if graph.neighbors(b).count() == k || graph.neighbors(a).count() == k {
continue;
}
Now that we know it's a valid edge, actually add it:
graph.add_edge(a, b, Edge {});
If we've reached the maximum number of edges => stop adding edges
if graph.edge_count() == num_edges {
print!("Have enough edges, exiting");
break 'make_cliques;
}
}
}
y_idx += clique_degree;
}
}
Hope that helps! Maybe someone can convert the rust to ruby. The full, running code is available on the playground.
Example output with m=84035, n=10, k=5, c=6859, a=8401, b_vec: [0, 1, 1, 3, 6]
Link to Dotgraph visualisation
Values: m=84035, n=10, k=5, c=6859, a=8401, b_vec: [0, 1, 1, 3, 6]
Making 6 cliques of degree 5
Making 3 cliques of degree 4
Making 1 cliques of degree 3
Making 1 cliques of degree 2
Making 0 cliques of degree 1
Dotstring of graph:
graph {
0 [ label = "Node" ]
1 [ label = "Node" ]
2 [ label = "Node" ]
3 [ label = "Node" ]
4 [ label = "Node" ]
5 [ label = "Node" ]
6 [ label = "Node" ]
7 [ label = "Node" ]
8 [ label = "Node" ]
9 [ label = "Node" ]
0 -- 1 [ ]
1 -- 2 [ ]
2 -- 3 [ ]
3 -- 4 [ ]
4 -- 5 [ ]
5 -- 6 [ ]
6 -- 7 [ ]
7 -- 8 [ ]
8 -- 9 [ ]
9 -- 3 [ ]
9 -- 7 [ ]
9 -- 1 [ ]
9 -- 0 [ ]
3 -- 7 [ ]
3 -- 1 [ ]
7 -- 1 [ ]
8 -- 2 [ ]
8 -- 0 [ ]
2 -- 0 [ ]
4 -- 6 [ ]
4 -- 0 [ ]
5 -- 2 [ ]
4 -- 8 [ ]
}

Related

Efficient algorithm for converting a "pop list" into an "index list"

Suppose I have a list of items:
[ A, B, C, D ]
and a "pop list":
[ 2, 0, 1, 0 ]
Let f(x,p) = y be a function that pops the indices p from x into a new list, y.
Using this process, you can compute
f([ A, B, C, D ], [ 2, 0, 1, 0 ]) = [ C, A, D, B ]
However, the cost of f is impractical, because it pops from a list and joins the remaining elements repeatedly.
It would be desirable to have an algorithm, g, to convert the pop list into a list of indices, such that
g(p) = [ 2, 0, 3, 1]
This would allow the new list to be constructed efficiently.
Is there an efficient algorithm, perhaps O(N), which could be used in implement g?
The easy way is to apply f to a list of indexes:
g(p) = f([0, 1, ... length(p)-1], p)
(assuming length(p) is the range of indexes. Otherwise use the appropriate length, or dynamically grow it if necessary)
This is O(n^2). You can make it O(n log n) by storing x in an order statistic tree instead of a list:
https://en.wikipedia.org/wiki/Order_statistic_tree
I wrote a small snippet in JS to explain my idea. The algorithm is NOT O(N) but probably O(n^2). It doesn't need to execute f though. I am not 100% certain this works properly but if not this might serve as an idea to build upon.
You reverse your pop list and iterate it:
Add current element to new array
Check whether previously added elements are bigger or equal to current element.
If true, increment the respective values.
Return newly filled array (reversed).
Rough sequence:
0
0 1
0 1 0
1 2 0 (increment old values with >= 0)
1 2 0 2
1 3 0 2 (increment old values with >= 2)
const data = [2, 0, 1, 0];
function popToIdx(popList) {
let arr = [],
tmp = [...popList].reverse(); // 0 1 0 2
for (let i = 0; i < tmp.length; i++) {
arr[i] = tmp[i];
for (let j = 0; j < i; j++) {
if (arr[j] >= tmp[i]) {
arr[j] += 1;
}
}
}
return arr.reverse();
}
console.log(popToIdx(data)) // 2 0 3 1
It works the same with lists since adding a new element to the end is still only O(1) and the partial iteration for lists and arrays is the same, too.

Check whether 2 nodes have any common parent(s) in a DAG graph

The input is:
An int[][], each sub array contains 2 int as {parent, child}, means there is a path from parent -> child.
e.g
{ { 1, 3 }, { 2, 3 }, { 3, 6 }, { 5, 6 }, { 5, 7 }, { 4, 5 }, { 4, 8 }, { 8, 9 } };
Or as a tree structure:
1 2 4
\ / / \
3 5 8
\ / \ \
6 7 9
The task is:
Giving 2 value (x, y), return a boolean value, to indicate whether they have any common parent(s).
Sample input and output:
[3, 8] => false
[5, 8] => true
[6, 8] => true
My idea:
Represent the input data as a DAG graph, in which data are stored in a Map like this Map<Integer, LinkedList<Integer>>, where key is the vertex, value is its adjacency list. And the direction in graph is reversed (compared to input data) as child -> parent, so that easy to search for parent.
Use a function findAvailableParents(Integer vertex) to find all parents (direct and indirect) for a single vertex, and return Set<Integer>.
Thus, only need to call findAvailableParents() once for each input vertex, then compare whether the 2 returned Sets have any intersection. If yes, then they have common parent; otherwise, they don't.
My questions are:
The time complexity in the solution above is between O(1) ~ O(E), right? (E is edge counts in the graph)
Is there a better solution?
A modified BFS might help you to solve the problem
Algorithm: checkCommonParent
def checkCommonParent(G, v1, v2):
# Create a queues for levelorder traversal
q1 = []
# Mark all the vertices as not visited
# This will be used to cover all the parts of graph
visited = [False]*(len(G.Vertices))
for v in G.Vertices:
if visited[v] == False:
q1.append(v)
visited[v] = True
# Check a connected component and see if it has both vertices exists.
# If it exists, that means they have a common ancestor
v1Visited = False
v2Visited = False
while ((len(q1) > 0) or (len(q2) > 0)):
while len(q1) > 0:
curVertex = q1.popleft()
for adjV in curVertex.adjecentVertices:
if visited[adjV] == False:
q1.append(adjV)
visited[adjV] = True
if adjV == v1:
v1Visited = True
elif adjV == v2:
v2Visited = True
if v1Visited and v2Visited:
return True
return False
I guess the idea is clear on the modification of BFS. Hope it helps!
suppose you have multiple inputs, now BFS would take around O(E) time to process each input.
All inputs can be queried in O(logn) if we do some pre computation which should take about O(nlogn) time
basically you want to find what is the Least common ancestor of those nodes
this thread in topcoder discusses the logic for a tree which can be extended to a DAG
You can also refer to this question for some further ideas
If an LCA exists between 2 nodes, then they have a common parent

Min Path Sum in Matrix- Brute Force

I'm working on the following problem:
Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path.
Note: You can only move either down or right at any point in time.
My initial impression here was to, from each position in the grid, get the min length of going to the right vs going downward. However, this gives me the incorrect answer for the following:
Input:
[[1,2],[1,1]]
Output:
2
Expected:
3
Intuitively, not sure what I'm doing wrong. It's also very simple code (I know it's not memoized--was planning on that for the next step) but intuitively not sure what's going wrong. The recursive base case makes sense, and each number is being taken into consideration.
def min_path_sum(grid)
smallest_path(0, 0, grid)
end
def smallest_path(row, col, grid)
return 0 if (row == grid.length || col == grid.first.length)
current_val = grid[row][col]
[current_val + smallest_path(row+1, col, grid), current_val + smallest_path(row, col+1, grid)].min #memoize here
end
You didn't make a proper termination condition. You check only until you hit either the right column or bottom row. You need to stay within bounds, but continue until you hit the lower-right corner. You need to recur within bounds until you hit both limits.
Given that, your code does work okay: it finds the path of 2 to the bottom row, rather than the path of 3 to the right edge. You just have to teach it to finish the job.
Is that enough to move you to a solution?
As this is a shortest path problem on an acyclic directed graph, you could use a standard shortest path algorithm.
You could also use dynamic programming ("DP), which may be the most efficient optimization technique. My answer implements a DP algorithm.
A shortest-path or DP algorithm would be vastly superior to enumerating all paths from top-left to bottom-right. As the number of paths increases exponentially with the size of the array, simple enumeration could only be used on modest-sized arrays.
The idea of the DP algorithm is as follows. Let n and m be the numbers of rows and columns, respectively. First compute the shortest path from each column in the last row to the last column in the last row. This is an easy calculation because there is only one path to [m-1, n-1] for each of these elements. Starting with [m-1, n-2] we simply work back to [m-1, 0].
Next we compute the shortest paths from each element in each of the other rows to [m-1, n-1], starting with the penultimate row (m-2) and working back to the first row (0). The last element in each row, [i, n-1], is an easy calculation because one can only go down (to [i+1, n-1]). Therefore, the shortest path from [i, n-1] to [m-1, n-1] is first going to [i+1, n-1] and then following the shortest path from [i+1, n-1], which we've already computed (including its length, of course). The length of the shortest path from [i, n-1] is the "down" distance for [i, n-1] plus the length of the shortest path from [i+1, n-1].
For elements [i, j], n-1,i < j < m-1, we calculate the shortest paths if we go right and down, and select the shorter of the two.
We can implement this as follows.
Code
def shortest_path(distance)
last_row, last_col = distance.size-1, distance.first.size-1
h = {}
last_row.downto(0) do |i|
last_col.downto(0) do |j|
h_right = { min_path_len: distance[i][j][:r] + h[[i,j+1]][:min_path_len],
next_node: [i,j+1] } if j < last_col
h_down = { min_path_len: distance[i][j][:d] + h[[i+1,j]][:min_path_len],
next_node: [i+1,j] } if i < last_row
g =
case
when i == last_row && j == last_col
{ min_path_len: 0, next_node: nil }
when i == last_row
h_right
when j == last_col
h_down
else
[h_right, h_down].min_by { |f| f[:min_path_len] }
end
h[[i,j]] = g
end
end
build_path(h)
end
def build_path(h)
node = [0, 0]
len = h[node][:min_path_len]
arr = []
while h[node][:next_node]
arr << node
node = h[node][:next_node]
end
[len, arr]
end
Example
Suppose these are the distances between adjacent nodes.
● 4 ● 3 ● 1 ● 2 ●
6 2 5 4 5
● 3 ● 4 ● 6 ● 3 ●
1 3 4 2 3
● 6 ● 3 ● 1 ● 2 ●
It's convenient to provide this information in the form of an array of hashes.
distance = [
[{ r: 4, d: 6 }, { r: 3, d: 2 }, { r: 1, d: 5 }, { r: 2, d: 4 }, { d: 5 }],
[{ r: 3, d: 1 }, { r: 4, d: 3 }, { r: 6, d: 4 }, { r: 3, d: 2 }, { d: 3 }],
[{ r: 6 }, { r: 3 }, { r: 1 }, { r: 2 }]
]
We may now compute a shortest path.
p shortest_path distance
#=> [15, [[0, 0], [0, 1], [1, 1], [2, 1], [2, 2], [2, 3]]]
A shortest path is given by the second element of the array that is returned. 15 is the length of that path.

How to sort three variables using at most two swaps?

The following algorithm can sort three variables x, y and z of type K which are comparable using operator<:
void sort2(K& x, K& y) {
if(y < x)
swap(x, y);
}
void sort3(K& x, K& y, K& z) {
sort2(x, y);
sort2(y, z);
sort2(x, y);
}
This needs three swaps in the "worst case". However basic mathematics tells us, that the ordering of three values can be done using only two swaps.
Example: The values (c,b,a) will be sorted using three swaps: (c,b,a) -> (b,c,a) -> (b,a,c) -> (a,b,c). However one swap would have been enough: (c,b,a) -> (a,b,c).
What would be the simplest algorithms which sorts three variables with at most two swaps in all cases?
Find the smallest, this takes 2 comparisons, and swap it into the first position.
Then compare the remaining 2 and swap if necessary.
if (x < y) {
if (z < x) swap(x,z);
} else {
if (y < z) swap(x,y);
else swap(x,z);
}
if(z<y) swap(y,z);
This takes 3 comparisons, but only two swaps.
void sort(int& a, int& b, int& c)
{
swap(a, min(a, min(b, c)));
swap(b, min(b, c));
}
2 swaps, 3 comparisons.
2 to 3 comparisons, 0 to ~1.7 swaps
Old question, new answer... The following algorithm sorts x, y and z with 2 to 3 comparisons depending on their values and 0 to ~1.7 swap operations.
void sort3(K& x, K& y, K& z)
{
if (y < x) {
if (z < x) {
if (z < y) {
swap(x, z);
} else {
K tmp = std::move(x);
x = std::move(y);
y = std::move(z);
z = std::move(tmp);
}
} else {
swap(x, y);
}
} else {
if (z < y) {
if (z < x) {
K tmp = std::move(z);
z = std::move(y);
y = std::move(x);
x = std::move(tmp);
} else {
swap(y, z);
}
}
}
}
So, how does it work? It's basiccaly an unrolled insertion sort: if the values are already sorted (it takes 2 comparisons to check that) then the algorithm does not swap anything. Otherwise, it performs 1 or 2 swap operations. However, when 2 swap operations are required, the algorithm « rotates » the values instead so that 4 moves are performed instead of 6 (a swap operation should cost 3 moves, unless optimized).
There are only 6 possible permutations of 3 values. This algorithm does the comparisons needed to know which permutation we're treating. Then it does the swapping and leaves. Therefore, the algorithm has 6 possible paths (including the one where it does nothing because the array is already sorted). While it's still human-readable, an equivalently optimal algorithm to sort 4 values would have 24 different paths and would be much harder to read (for n values, there are n! possible permutations).
Since we're already in 2015 and you seemed to be using C++, I took the liberty use std::move so to make sure that the swap-rotate thingy would be efficient enough and would work even for moveable but non-copyable types.
Find the minimum value and swap it with the first value. Find the second minimum and swap it with the second value. Two swaps at most.
This is basically selection sort, which will perform at most n - 1 swaps.
If you don't do it in place, you can perform it without any swaps.
Encode a sorting network in a table. The Wikipedia article I linked should help you with references in case you need to figure out what to put in the table in other cases (i.e., bigger arrays).
I think what you want is to find the optimal swap in each step instead of just a valid swap. To do that, just find the greatest difference between an element and an element later in the list and swap those. In a 3-tuple, there are three possible swaps, 1-3, 1-2, and 2-3. At each step find the max difference among these three swaps and do that. Pretty sure that gives two swaps in the worst case for 3 elements. Only really makes sense if swapping is relatively expensive compared to comparing elements, otherwise probably not worth the additional analysis upfront.
Cool question :)
If assembly is available to you, and the values fit in a register, then you can probably do it extremely fast by just loading them into registers and doing a few compares, jumping to the right scenario to put the values back. Maybe your compiler makes this optimization already.
Either way, if performance is your goal, take a look at the generated machine code and optimize there. For such a small algorithm that's where you can squeeze performance out of.
I recently had to solve a similar problem - sort three values efficiently. You concentrate on swap-operations in your question. If performance is what you are looking for, concentrate on the comparison operations and branches! When sorting such a "tiny" array with just three values, a good idea is to consider using additional storage, which is appropriate for so few values. I came up with something like a specialized "merge sort" (see code below).
Just as tenfour suggests, I looked at the assembly, and the code below compiles down to a compact inline set of CPU-register operations, and is extremely fast. The additional variable "arr12" is also stored in the CPU-registers. The sorting requires two or three comparison operations. The function can easily be converted to a template (not given here for clarity).
inline void sort3_descending( double * arr )
{
double arr12[ 2 ];
// sort first two values
if( arr[ 0 ] > arr[ 1 ] )
{
arr12[ 0 ] = arr[ 0 ];
arr12[ 1 ] = arr[ 1 ];
} // if
else
{
arr12[ 0 ] = arr[ 1 ];
arr12[ 1 ] = arr[ 0 ];
} // else
// decide where to put arr12 and the third original value arr[ 3 ]
if( arr12[ 1 ] > arr[ 2 ] )
{
arr[ 0 ] = arr12[ 0 ];
arr[ 1 ] = arr12[ 1 ];
} // if
else if( arr[ 2 ] > arr12[ 0 ] )
{
arr[ 0 ] = arr [ 2 ];
arr[ 1 ] = arr12[ 0 ];
arr[ 2 ] = arr12[ 1 ];
} // if
else
{
arr[ 0 ] = arr12[ 0 ];
arr[ 1 ] = arr [ 2 ];
arr[ 2 ] = arr12[ 1 ];
} // else
}
This can illustrated with a truth table relating to every possible combination of comparisons to see how we can best optimize the swap you mention here.
Values | x < y | y < z | x < z
x,y,z | y | y | y
x,z,y | y | n | y
y,x,z | n | y | y
y,z,x | n | y | n
z,x,y | y | n | n
z,y,x | n | n | n
By framing the question this way, we can easily see that by initially checking and swapping the 1st and 3rd element, the lowest value that we can have in the first element after the swap can either be x or y. This simplifies the if check afterwards so that we can either swap the 1st and 2nd element when x > y or swap the 2nd and 3rd element when y > z.
if (x > z) {
swap(x,z);
}
if (x > y) {
swap(x,y);
} else if (y > z) {
swap(y,z);
}
No need for any nested if conditionals. Just 2-3 simple comparisons for 2 swaps at max.

How can you compare to what extent two lists are in the same order?

I have two arrays containing the same elements, but in different orders, and I want to know the extent to which their orders differ.
The method I tried, didn't work. it was as follows:
For each list I built a matrix which recorded for each pair of elements whether they were above or below each other in the list. I then calculated a pearson correlation coefficient of these two matrices. This worked extremely badly. Here's a trivial example:
list 1:
1
2
3
4
list 2:
1
3
2
4
The method I described above produced matrices like this (where 1 means the row number is higher than the column, and 0 vice-versa):
list 1:
1 2 3 4
1 1 1 1
2 1 1
3 1
4
list 2:
1 2 3 4
1 1 1 1
2 0 1
3 1
4
Since the only difference is the order of elements 2 and 3, these should be deemed to be very similar. The Pearson Correlation Coefficient for those two matrices is 0, suggesting they are not correlated at all. I guess the problem is that what I'm looking for is not really a correlation coefficient, but some other kind of similarity measure. Edit distance, perhaps?
Can anyone suggest anything better?
Mean square of differences of indices of each element.
List 1: A B C D E
List 2: A D C B E
Indices of each element of List 1 in List 2 (zero based)
A B C D E
0 3 2 1 4
Indices of each element of List 1 in List 1 (zero based)
A B C D E
0 1 2 3 4
Differences:
A B C D E
0 -2 0 2 0
Square of differences:
A B C D E
4 4
Average differentness = 8 / 5.
Just an idea, but is there any mileage in adapting a standard sort algorithm to count the number of swap operations needed to transform list1 into list2?
I think that defining the compare function may be difficult though (perhaps even just as difficult as the original problem!), and this may be inefficient.
edit: thinking about this a bit more, the compare function would essentially be defined by the target list itself. So for example if list 2 is:
1 4 6 5 3
...then the compare function should result in 1 < 4 < 6 < 5 < 3 (and return equality where entries are equal).
Then the swap function just needs to be extended to count the swap operations.
A bit late for the party here, but just for the record, I think Ben almost had it... if you'd looked further into correlation coefficients, I think you'd have found that Spearman's rank correlation coefficient might have been the way to go.
Interestingly, jamesh seems to have derived a similar measure, but not normalized.
See this recent SO answer.
You might consider how many changes it takes to transform one string into another (which I guess it was you were getting at when you mentioned edit distance).
See: http://en.wikipedia.org/wiki/Levenshtein_distance
Although I don't think l-distance takes into account rotation. If you allow rotation as an operation then:
1, 2, 3, 4
and
2, 3, 4, 1
Are pretty similar.
There is a branch-and-bound algorithm that should work for any set of operators you like. It may not be real fast. The pseudocode goes something like this:
bool bounded_recursive_compare_routine(int* a, int* b, int level, int bound){
if (level > bound) return false;
// if at end of a and b, return true
// apply rule 0, like no-change
if (*a == *b){
bounded_recursive_compare_routine(a+1, b+1, level+0, bound);
// if it returns true, return true;
}
// if can apply rule 1, like rotation, to b, try that and recur
bounded_recursive_compare_routine(a+1, b+1, level+cost_of_rotation, bound);
// if it returns true, return true;
...
return false;
}
int get_minimum_cost(int* a, int* b){
int bound;
for (bound=0; ; bound++){
if (bounded_recursive_compare_routine(a, b, 0, bound)) break;
}
return bound;
}
The time it takes is roughly exponential in the answer, because it is dominated by the last bound that works.
Added: This can be extended to find the nearest-matching string stored in a trie. I did that years ago in a spelling-correction algorithm.
I'm not sure exactly what formula it uses under the hood, but difflib.SequenceMatcher.ratio() does exactly this:
ratio(self) method of difflib.SequenceMatcher instance:
Return a measure of the sequences' similarity (float in [0,1]).
Code example:
from difflib import SequenceMatcher
sm = SequenceMatcher(None, '1234', '1324')
print sm.ratio()
>>> 0.75
Another approach that is based on a little bit of mathematics is to count the number of inversions to convert one of the arrays into the other one. An inversion is the exchange of two neighboring array elements. In ruby it is done like this:
# extend class array by new method
class Array
def dist(other)
raise 'can calculate distance only to array with same length' if length != other.length
# initialize count of inversions to 0
count = 0
# loop over all pairs of indices i, j with i<j
length.times do |i|
(i+1).upto(length) do |j|
# increase count if i-th and j-th element have different order
count += 1 if (self[i] <=> self[j]) != (other[i] <=> other[j])
end
end
return count
end
end
l1 = [1, 2, 3, 4]
l2 = [1, 3, 2, 4]
# try an example (prints 1)
puts l1.dist(l2)
The distance between two arrays of length n can be between 0 (they are the same) and n*(n+1)/2 (reversing the first array one gets the second). If you prefer to have distances always between 0 and 1 to be able to compare distances of pairs of arrays of different length, just divide by n*(n+1)/2.
A disadvantage of this algorithms is it running time of n^2. It also assumes that the arrays don't have double entries, but it could be adapted.
A remark about the code line "count += 1 if ...": the count is increased only if either the i-th element of the first list is smaller than its j-th element and the i-th element of the second list is bigger than its j-th element or vice versa (meaning that the i-th element of the first list is bigger than its j-th element and the i-th element of the second list is smaller than its j-th element). In short: (l1[i] < l1[j] and l2[i] > l2[j]) or (l1[i] > l1[j] and l2[i] < l2[j])
If one has two orders one should look at two important ranking correlation coefficients:
Spearman's rank correlation coefficient: https://en.wikipedia.org/wiki/Spearman%27s_rank_correlation_coefficient
This is almost the same as Jamesh answer but scaled in the range -1 to 1.
It is defined as:
1 - ( 6 * sum_of_squared_distances ) / ( n_samples * (n_samples**2 - 1 )
Kendalls tau: https://nl.wikipedia.org/wiki/Kendalls_tau
When using python one could use:
from scipy import stats
order1 = [ 1, 2, 3, 4]
order2 = [ 1, 3, 2, 4]
print stats.spearmanr(order1, order2)[0]
>> 0.8000
print stats.kendalltau(order1, order2)[0]
>> 0.6667
if anyone is using R language, I've implemented a function that computes the "spearman rank correlation coefficient" using the method described above by #bubake here:
get_spearman_coef <- function(objectA, objectB) {
#getting the spearman rho rank test
spearman_data <- data.frame(listA = objectA, listB = objectB)
spearman_data$rankA <- 1:nrow(spearman_data)
rankB <- c()
for (index_valueA in 1:nrow(spearman_data)) {
for (index_valueB in 1:nrow(spearman_data)) {
if (spearman_data$listA[index_valueA] == spearman_data$listB[index_valueB]) {
rankB <- append(rankB, index_valueB)
}
}
}
spearman_data$rankB <- rankB
spearman_data$distance <-(spearman_data$rankA - spearman_data$rankB)**2
spearman <- 1 - ( (6 * sum(spearman_data$distance)) / (nrow(spearman_data) * ( nrow(spearman_data)**2 -1) ) )
print(paste("spearman's rank correlation coefficient"))
return( spearman)
}
results :
get_spearman_coef(c("a","b","c","d","e"), c("a","b","c","d","e"))
spearman's rank correlation coefficient: 1
get_spearman_coef(c("a","b","c","d","e"), c("b","a","d","c","e"))
spearman's rank correlation coefficient: 0.9

Resources