Finding all cycles in undirected graphs - graph
I need a working algorithm for finding all simple cycles in an undirected graph. I know the cost can be exponential and the problem is NP-complete, but I am going to use it in a small graph (up to 20-30 vertices) and the cycles are small in number.
After a long research (mainly here) I still don't have a working approach. Here is a summary of my search:
Finding all cycles in an undirected graph
Cycles in an Undirected Graph -> detects only whether there is a cycle or not
Finding polygons within an undirected Graph -> very nice description, but no solution
Finding all cycles in a directed graph -> finds cycles only in directed graphs
Detect cycles in undirected graph using boost graph library
The only answer I found, which approaches my problem, is this one:
Find all cycles in graph, redux
It seems that finding a basic set of cycles and XOR-ing them could do the trick. Finding a basic set of cycles is easy, but I don't understand how to combine them in order to obtain all cycles in the graph...
For an undirected graph the standard approach is to look for a so called cycle base : a set of simple cycles from which one can generate through combinations all other cycles. These are not necessarily all simple cycles in the graph. Consider for example the following graph:
A
/ \
B ----- C
\ /
D
There are 3 simple cycles here : A-B-C-A, B-C-D-B and A-B-D-C-A. You can however take each 2 of these as a basis and obtain the 3rd as a combination of the 2. This is a substantial difference from directed graphs where one can not combine so freely cycles due to the need to observe edge direction.
The standard baseline algorithm for finding a cycle base for an undirected graph is this : Build a spanning tree and then for each edge which is not part of the tree build a cycle from that edge and some edges on the tree. Such cycle must exist because otherwise the edge would be part of the tree.
For example one of the possible spanning trees for the sample graph above is this:
A
/ \
B C
\
D
The 2 edges not in the tree are B-C and C-D. And the corresponding simple cycles are A-B-C-A and A-B-D-C-A.
You can also build the following spanning tree:
A
/
B ----- C
\
D
And for this spanning tree the simple cycles would be A-B-C-A and B-C-D-B.
The baseline algorithm can be refined in different ways. To the best of my knowledge the best refinement belongs to Paton (K. Paton, An algorithm for finding a fundamental set of cycles for an undirected linear graph, Comm. ACM 12 (1969), pp. 514-518.). An open source implementation in Java is available here : http://code.google.com/p/niographs/ .
I should have mentioned how you combine simple cycles from the cycle base to form new simple cycles. You start off by listing in any (but fixed hereafter) order all edges of the graph. Then you represent cycles by sequences of zeros and ones by placing ones in the positions of edges which belong to the cycle and zeros in the positions of edges which are not part of the cycle. Then you do bitwise exclusive OR (XOR) of the sequences. The reason you do XOR is that you want to exclude edges which belong to both cycles and thus make the combined cycle non-simple. You need to check also that the 2 cycles have SOME common edges by checking that the bitwise AND of the sequences is not all zeros. Otherwise the result of XOR will be 2 disjoint cycles rather than a new simple cycle.
Here is an example for the sample graph above:
We start by listing the edges : ((AB), (AC), (BC), (BD), (CD)). Then the simple cycles A-B-C-A, B-D-C-B and A-B-D-C-A are represented as (1, 1, 1, 0, 0), (0, 0, 1, 1, 1) and (1, 1, 0, 1, 1). Now we can for example XOR A-B-C-A with B-D-C-B and the result is (1, 1, 0, 1, 1) which is exactly A-B-D-C-A. Or we can XOR A-B-C-A and A-B-D-C-A with the result being (0, 0, 1, 1, 1). Which is exactly B-D-C-B.
Given a cycle base you can discover all simple cycles by examining all possible combinations of 2 or more distinct base cycles. The procedure is described in more detail here : http://dspace.mit.edu/bitstream/handle/1721.1/68106/FTL_R_1982_07.pdf on page 14.
For the sake of completeness, I would notice that it seems possible (and inefficient) to use algorithms for finding all simple cycles of a directed graph. Every edge of the undirected graph can be replaced by 2 directed edges going in opposite directions. Then algorithms for directed graphs should work. There will be 1 "false" 2-node cycle for every edge of the undirected graph which will have to be ignored and there will be a clockwise and a counterclockwise version of every simple cycle of the undirected graph. Open source implementation in Java of algorithms for finding all cycles in a directed graph can be found at the link I already quoted.
Axel, I've translated your code to python. About 1/4th the lines of code and clearer to read.
graph = [[1, 2], [1, 3], [1, 4], [2, 3], [3, 4], [2, 6], [4, 6], [8, 7], [8, 9], [9, 7]]
cycles = []
def main():
global graph
global cycles
for edge in graph:
for node in edge:
findNewCycles([node])
for cy in cycles:
path = [str(node) for node in cy]
s = ",".join(path)
print(s)
def findNewCycles(path):
start_node = path[0]
next_node= None
sub = []
#visit each edge and each node of each edge
for edge in graph:
node1, node2 = edge
if start_node in edge:
if node1 == start_node:
next_node = node2
else:
next_node = node1
if not visited(next_node, path):
# neighbor node not on path yet
sub = [next_node]
sub.extend(path)
# explore extended path
findNewCycles(sub);
elif len(path) > 2 and next_node == path[-1]:
# cycle found
p = rotate_to_smallest(path);
inv = invert(p)
if isNew(p) and isNew(inv):
cycles.append(p)
def invert(path):
return rotate_to_smallest(path[::-1])
# rotate cycle path such that it begins with the smallest node
def rotate_to_smallest(path):
n = path.index(min(path))
return path[n:]+path[:n]
def isNew(path):
return not path in cycles
def visited(node, path):
return node in path
main()
The following is a demo implementation in C# (and Java, see end of answer) based on depth first search.
An outer loop scans all nodes of the graph and starts a search from every node. Node neighbours (according to the list of edges) are added to the cycle path. Recursion ends if no more non-visited neighbours can be added. A new cycle is found if the path is longer than two nodes and the next neighbour is the start of the path. To avoid duplicate cycles, the cycles are normalized by rotating the smallest node to the start. Cycles in inverted ordering are also taken into account.
This is just a naive implementation.
The classical paper is: Donald B. Johnson. Finding all the elementary circuits of a directed graph. SIAM J. Comput., 4(1):77–84, 1975.
A recent survey of modern algorithms can be found here
using System;
using System.Collections.Generic;
namespace akCyclesInUndirectedGraphs
{
class Program
{
// Graph modelled as list of edges
static int[,] graph =
{
{1, 2}, {1, 3}, {1, 4}, {2, 3},
{3, 4}, {2, 6}, {4, 6}, {7, 8},
{8, 9}, {9, 7}
};
static List<int[]> cycles = new List<int[]>();
static void Main(string[] args)
{
for (int i = 0; i < graph.GetLength(0); i++)
for (int j = 0; j < graph.GetLength(1); j++)
{
findNewCycles(new int[] {graph[i, j]});
}
foreach (int[] cy in cycles)
{
string s = "" + cy[0];
for (int i = 1; i < cy.Length; i++)
s += "," + cy[i];
Console.WriteLine(s);
}
}
static void findNewCycles(int[] path)
{
int n = path[0];
int x;
int[] sub = new int[path.Length + 1];
for (int i = 0; i < graph.GetLength(0); i++)
for (int y = 0; y <= 1; y++)
if (graph[i, y] == n)
// edge referes to our current node
{
x = graph[i, (y + 1) % 2];
if (!visited(x, path))
// neighbor node not on path yet
{
sub[0] = x;
Array.Copy(path, 0, sub, 1, path.Length);
// explore extended path
findNewCycles(sub);
}
else if ((path.Length > 2) && (x == path[path.Length - 1]))
// cycle found
{
int[] p = normalize(path);
int[] inv = invert(p);
if (isNew(p) && isNew(inv))
cycles.Add(p);
}
}
}
static bool equals(int[] a, int[] b)
{
bool ret = (a[0] == b[0]) && (a.Length == b.Length);
for (int i = 1; ret && (i < a.Length); i++)
if (a[i] != b[i])
{
ret = false;
}
return ret;
}
static int[] invert(int[] path)
{
int[] p = new int[path.Length];
for (int i = 0; i < path.Length; i++)
p[i] = path[path.Length - 1 - i];
return normalize(p);
}
// rotate cycle path such that it begins with the smallest node
static int[] normalize(int[] path)
{
int[] p = new int[path.Length];
int x = smallest(path);
int n;
Array.Copy(path, 0, p, 0, path.Length);
while (p[0] != x)
{
n = p[0];
Array.Copy(p, 1, p, 0, p.Length - 1);
p[p.Length - 1] = n;
}
return p;
}
static bool isNew(int[] path)
{
bool ret = true;
foreach(int[] p in cycles)
if (equals(p, path))
{
ret = false;
break;
}
return ret;
}
static int smallest(int[] path)
{
int min = path[0];
foreach (int p in path)
if (p < min)
min = p;
return min;
}
static bool visited(int n, int[] path)
{
bool ret = false;
foreach (int p in path)
if (p == n)
{
ret = true;
break;
}
return ret;
}
}
}
The cycles for the demo graph:
1,3,2
1,4,3,2
1,4,6,2
1,3,4,6,2
1,4,6,2,3
1,4,3
2,6,4,3
7,9,8
The algorithm coded in Java:
import java.util.ArrayList;
import java.util.List;
public class GraphCycleFinder {
// Graph modeled as list of edges
static int[][] graph =
{
{1, 2}, {1, 3}, {1, 4}, {2, 3},
{3, 4}, {2, 6}, {4, 6}, {7, 8},
{8, 9}, {9, 7}
};
static List<int[]> cycles = new ArrayList<int[]>();
/**
* #param args
*/
public static void main(String[] args) {
for (int i = 0; i < graph.length; i++)
for (int j = 0; j < graph[i].length; j++)
{
findNewCycles(new int[] {graph[i][j]});
}
for (int[] cy : cycles)
{
String s = "" + cy[0];
for (int i = 1; i < cy.length; i++)
{
s += "," + cy[i];
}
o(s);
}
}
static void findNewCycles(int[] path)
{
int n = path[0];
int x;
int[] sub = new int[path.length + 1];
for (int i = 0; i < graph.length; i++)
for (int y = 0; y <= 1; y++)
if (graph[i][y] == n)
// edge refers to our current node
{
x = graph[i][(y + 1) % 2];
if (!visited(x, path))
// neighbor node not on path yet
{
sub[0] = x;
System.arraycopy(path, 0, sub, 1, path.length);
// explore extended path
findNewCycles(sub);
}
else if ((path.length > 2) && (x == path[path.length - 1]))
// cycle found
{
int[] p = normalize(path);
int[] inv = invert(p);
if (isNew(p) && isNew(inv))
{
cycles.add(p);
}
}
}
}
// check of both arrays have same lengths and contents
static Boolean equals(int[] a, int[] b)
{
Boolean ret = (a[0] == b[0]) && (a.length == b.length);
for (int i = 1; ret && (i < a.length); i++)
{
if (a[i] != b[i])
{
ret = false;
}
}
return ret;
}
// create a path array with reversed order
static int[] invert(int[] path)
{
int[] p = new int[path.length];
for (int i = 0; i < path.length; i++)
{
p[i] = path[path.length - 1 - i];
}
return normalize(p);
}
// rotate cycle path such that it begins with the smallest node
static int[] normalize(int[] path)
{
int[] p = new int[path.length];
int x = smallest(path);
int n;
System.arraycopy(path, 0, p, 0, path.length);
while (p[0] != x)
{
n = p[0];
System.arraycopy(p, 1, p, 0, p.length - 1);
p[p.length - 1] = n;
}
return p;
}
// compare path against known cycles
// return true, iff path is not a known cycle
static Boolean isNew(int[] path)
{
Boolean ret = true;
for(int[] p : cycles)
{
if (equals(p, path))
{
ret = false;
break;
}
}
return ret;
}
static void o(String s)
{
System.out.println(s);
}
// return the int of the array which is the smallest
static int smallest(int[] path)
{
int min = path[0];
for (int p : path)
{
if (p < min)
{
min = p;
}
}
return min;
}
// check if vertex n is contained in path
static Boolean visited(int n, int[] path)
{
Boolean ret = false;
for (int p : path)
{
if (p == n)
{
ret = true;
break;
}
}
return ret;
}
}
Here's just a very lame MATLAB version of this algorithm adapted from the python code above, for anyone who might need it as well.
function cycleList = searchCycles(edgeMap)
tic
global graph cycles numCycles;
graph = edgeMap;
numCycles = 0;
cycles = {};
for i = 1:size(graph,1)
for j = 1:2
findNewCycles(graph(i,j))
end
end
% print out all found cycles
for i = 1:size(cycles,2)
cycles{i}
end
% return the result
cycleList = cycles;
toc
function findNewCycles(path)
global graph cycles numCycles;
startNode = path(1);
nextNode = nan;
sub = [];
% visit each edge and each node of each edge
for i = 1:size(graph,1)
node1 = graph(i,1);
node2 = graph(i,2);
if node1 == startNode
nextNode = node2;
elseif node2 == startNode
nextNode = node1;
end
if ~(visited(nextNode, path))
% neighbor node not on path yet
sub = nextNode;
sub = [sub path];
% explore extended path
findNewCycles(sub);
elseif size(path,2) > 2 && nextNode == path(end)
% cycle found
p = rotate_to_smallest(path);
inv = invert(p);
if isNew(p) && isNew(inv)
numCycles = numCycles + 1;
cycles{numCycles} = p;
end
end
end
function inv = invert(path)
inv = rotate_to_smallest(path(end:-1:1));
% rotate cycle path such that it begins with the smallest node
function new_path = rotate_to_smallest(path)
[~,n] = min(path);
new_path = [path(n:end), path(1:n-1)];
function result = isNew(path)
global cycles
result = 1;
for i = 1:size(cycles,2)
if size(path,2) == size(cycles{i},2) && all(path == cycles{i})
result = 0;
break;
end
end
function result = visited(node,path)
result = 0;
if isnan(node) && any(isnan(path))
result = 1;
return
end
for i = 1:size(path,2)
if node == path(i)
result = 1;
break
end
end
Here is a C++ version of the python code above:
std::vector< std::vector<vertex_t> > Graph::findAllCycles()
{
std::vector< std::vector<vertex_t> > cycles;
std::function<void(std::vector<vertex_t>)> findNewCycles = [&]( std::vector<vertex_t> sub_path )
{
auto visisted = []( vertex_t v, const std::vector<vertex_t> & path ){
return std::find(path.begin(),path.end(),v) != path.end();
};
auto rotate_to_smallest = []( std::vector<vertex_t> path ){
std::rotate(path.begin(), std::min_element(path.begin(), path.end()), path.end());
return path;
};
auto invert = [&]( std::vector<vertex_t> path ){
std::reverse(path.begin(),path.end());
return rotate_to_smallest(path);
};
auto isNew = [&cycles]( const std::vector<vertex_t> & path ){
return std::find(cycles.begin(), cycles.end(), path) == cycles.end();
};
vertex_t start_node = sub_path[0];
vertex_t next_node;
// visit each edge and each node of each edge
for(auto edge : edges)
{
if( edge.has(start_node) )
{
vertex_t node1 = edge.v1, node2 = edge.v2;
if(node1 == start_node)
next_node = node2;
else
next_node = node1;
if( !visisted(next_node, sub_path) )
{
// neighbor node not on path yet
std::vector<vertex_t> sub;
sub.push_back(next_node);
sub.insert(sub.end(), sub_path.begin(), sub_path.end());
findNewCycles( sub );
}
else if( sub_path.size() > 2 && next_node == sub_path.back() )
{
// cycle found
auto p = rotate_to_smallest(sub_path);
auto inv = invert(p);
if( isNew(p) && isNew(inv) )
cycles.push_back( p );
}
}
}
};
for(auto edge : edges)
{
findNewCycles( std::vector<vertex_t>(1,edge.v1) );
findNewCycles( std::vector<vertex_t>(1,edge.v2) );
}
}
Inspired by #LetterRip and #Axel Kemper
Here is a shorter version of Java:
public static int[][] graph =
{
{1, 2}, {2, 3}, {3, 4}, {2, 4},
{3, 5}
};
public static Set<List<Integer>> cycles = new HashSet<>();
static void findNewCycles(ArrayList<Integer> path) {
int start = path.get(0);
int next = -1;
for (int[] edge : graph) {
if (start == edge[0] || start == edge[1]) {
next = (start == edge[0]) ? edge[1] : edge[0];
if (!path.contains(next)) {
ArrayList<Integer> newPath = new ArrayList<>();
newPath.add(next);
newPath.addAll((path));
findNewCycles(newPath);
} else if (path.size() > 2 && next == path.get(path.size() - 1)) {
List<Integer> normalized = new ArrayList<>(path);
Collections.sort(normalized);
cycles.add(normalized);
}
}
}
}
public static void detectCycle() {
for (int i = 0; i < graph.length; i++)
for (int j = 0; j < graph[i].length; j++) {
ArrayList<Integer> path = new ArrayList<>();
path.add(graph[i][j]);
findNewCycles(path);
}
for (List<Integer> c : cycles) {
System.out.println(c);
}
}
Here is a node version of the python code.
const graph = [[1, 2], [1, 3], [1, 4], [2, 3], [3, 4], [2, 6], [4, 6], [8, 7], [8, 9], [9, 7]]
let cycles = []
function main() {
for (const edge of graph) {
for (const node of edge) {
findNewCycles([node])
}
}
for (cy of cycles) {
console.log(cy.join(','))
}
}
function findNewCycles(path) {
const start_node = path[0]
let next_node = null
let sub = []
// visit each edge and each node of each edge
for (const edge of graph) {
const [node1, node2] = edge
if (edge.includes(start_node)) {
next_node = node1 === start_node ? node2 : node1
}
if (notVisited(next_node, path)) {
// eighbor node not on path yet
sub = [next_node].concat(path)
// explore extended path
findNewCycles(sub)
} else if (path.length > 2 && next_node === path[path.length - 1]) {
// cycle found
const p = rotateToSmallest(path)
const inv = invert(p)
if (isNew(p) && isNew(inv)) {
cycles.push(p)
}
}
}
}
function invert(path) {
return rotateToSmallest([...path].reverse())
}
// rotate cycle path such that it begins with the smallest node
function rotateToSmallest(path) {
const n = path.indexOf(Math.min(...path))
return path.slice(n).concat(path.slice(0, n))
}
function isNew(path) {
const p = JSON.stringify(path)
for (const cycle of cycles) {
if (p === JSON.stringify(cycle)) {
return false
}
}
return true
}
function notVisited(node, path) {
const n = JSON.stringify(node)
for (const p of path) {
if (n === JSON.stringify(p)) {
return false
}
}
return true
}
main()
Here is a vb .net version of the python code above:
Module Module1
' Graph modelled as list of edges
Public graph As Integer(,) = {{{1, 2}, {1, 3}, {1, 4}, {2, 3},
{3, 4}, {2, 6}, {4, 6}, {7, 8},
{8, 9}, {9, 7}}
Public cycles As New List(Of Integer())()
Sub Main()
For i As Integer = 0 To graph.GetLength(0) - 1
For j As Integer = 0 To graph.GetLength(1) - 1
findNewCycles(New Integer() {graph(i, j)})
Next
Next
For Each cy As Integer() In cycles
Dim s As String
s = cy(0)
For i As Integer = 1 To cy.Length - 1
s = s & "," & cy(i)
Next
Console.WriteLine(s)
Debug.Print(s)
Next
End Sub
Private Sub findNewCycles(path As Integer())
Dim n As Integer = path(0)
Dim x As Integer
Dim [sub] As Integer() = New Integer(path.Length) {}
For i As Integer = 0 To graph.GetLength(0) - 1
For y As Integer = 0 To 1
If graph(i, y) = n Then
' edge referes to our current node
x = graph(i, (y + 1) Mod 2)
If Not visited(x, path) Then
' neighbor node not on path yet
[sub](0) = x
Array.Copy(path, 0, [sub], 1, path.Length)
' explore extended path
findNewCycles([sub])
ElseIf (path.Length > 2) AndAlso (x = path(path.Length - 1)) Then
' cycle found
Dim p As Integer() = normalize(path)
Dim inv As Integer() = invert(p)
If isNew(p) AndAlso isNew(inv) Then
cycles.Add(p)
End If
End If
End If
Next
Next
End Sub
Private Function equals(a As Integer(), b As Integer()) As Boolean
Dim ret As Boolean = (a(0) = b(0)) AndAlso (a.Length = b.Length)
Dim i As Integer = 1
While ret AndAlso (i < a.Length)
If a(i) <> b(i) Then
ret = False
End If
i += 1
End While
Return ret
End Function
Private Function invert(path As Integer()) As Integer()
Dim p As Integer() = New Integer(path.Length - 1) {}
For i As Integer = 0 To path.Length - 1
p(i) = path(path.Length - 1 - i)
Next
Return normalize(p)
End Function
' rotate cycle path such that it begins with the smallest node
Private Function normalize(path As Integer()) As Integer()
Dim p As Integer() = New Integer(path.Length - 1) {}
Dim x As Integer = smallest(path)
Dim n As Integer
Array.Copy(path, 0, p, 0, path.Length)
While p(0) <> x
n = p(0)
Array.Copy(p, 1, p, 0, p.Length - 1)
p(p.Length - 1) = n
End While
Return p
End Function
Private Function isNew(path As Integer()) As Boolean
Dim ret As Boolean = True
For Each p As Integer() In cycles
If equals(p, path) Then
ret = False
Exit For
End If
Next
Return ret
End Function
Private Function smallest(path As Integer()) As Integer
Dim min As Integer = path(0)
For Each p As Integer In path
If p < min Then
min = p
End If
Next
Return min
End Function
Private Function visited(n As Integer, path As Integer()) As Boolean
Dim ret As Boolean = False
For Each p As Integer In path
If p = n Then
ret = True
Exit For
End If
Next
Return ret
End Function
End Module
It seems that the cycle finder above has some problems. The C# version fails to find some cycles. My graph is:
{2,8},{4,8},{5,8},{1,9},{3,9},{4,9},{5,9},{6,9},{1,10},
{4,10},{5,10},{6,10},{7,10},{1,11},{4,11},{6,11},{7,11},
{1,12},{2,12},{3,12},{5,12},{6,12},{2,13},{3,13},{4,13},
{6,13},{7,13},{2,14},{5,14},{7,14}
For example, the cycle: 1-9-3-12-5-10 is not found.
I tried the C++ version as well, it returns very large (tens of millions) number of cycles which is apparently wrong. Probably, it fails to match the cycles.
Sorry, I am in a bit of crunch and I have not investigated further. I wrote my own version based on post of Nikolay Ognyanov (thank you very much for your post). For the graph above my version returns 8833 cycles and I am trying to verify that it is correct. The C# version returns 8397 cycles.
This is NOT an answer!
#Nikolay Ognyano
1. Trying to understand how we should generate the combined cycles with simple cycles
I am trying to understand what you mentioned
You need to check also that the 2 cycles have SOME common edges by checking that the bitwise AND of the sequences is not all zeros. Otherwise the result of XOR will be 2 disjoint cycles rather than a new simple cycle.
I'd like to understand how we should deal with a graph like below:
0-----2-----4
| /| /
| / | /
| / | /
| / | /
|/ |/
1-----3
Assuming the fundamental/simple cycles are:
0 1 2
1 2 3
2 3 4
Apparently, if I use the following bitwise XOR and AND, it will miss the cycle 0 1 3 4 2.
bitset<MAX> ComputeCombinedCycleBits(const vector<bitset<MAX>>& bsets) {
bitset<MAX> bsCombo, bsCommonEdgeCheck; bsCommonEdgeCheck.set();
for (const auto& bs : bsets)
bsCombo ^= bs, bsCommonEdgeCheck &= bs;
if (bsCommonEdgeCheck.none()) bsCombo.reset();
return bsCombo;
}
I think the main issue is here bsCommonEdgeCheck &= bs? What should we use if there are more than 3 simple cycle to compose the combined cycle?
2. Trying to understand how we get the order of the combined cycle
For example, with the following graph:
0-----1
|\ /|
| \ / |
| X |
| / \ |
|/ \|
3-----2
Assuming the fundamental/simple cycles are:
0 1 2
0 2 3
0 1 3
After we use the bitwise XOR, we have completely lost the order of the simple cycles, and how can get the node order of the combined cycle?
The Matlab version missed something, function findNewCycles(path) should be:
function findNewCycles(path)
global graph cycles numCycles;
startNode = path(1);
nextNode = nan;
sub = [];
% visit each edge and each node of each edge
for i = 1:size(graph,1)
node1 = graph(i,1);
node2 = graph(i,2);
if (node1 == startNode) || (node2==startNode) %% this if is required
if node1 == startNode
nextNode = node2;
elseif node2 == startNode
nextNode = node1;
end
if ~(visited(nextNode, path))
% neighbor node not on path yet
sub = nextNode;
sub = [sub path];
% explore extended path
findNewCycles(sub);
elseif size(path,2) > 2 && nextNode == path(end)
% cycle found
p = rotate_to_smallest(path);
inv = invert(p);
if isNew(p) && isNew(inv)
numCycles = numCycles + 1;
cycles{numCycles} = p;
end
end
end
end
Related
2D array traversal to get distinct 7 digit number combos
I ran into a tricky question from an interview prep book which goes.. You have a 3 by 3 matrix containing integers 1 to 9 as shown below 1 2 3 4 5 6 7 8 9 How do you get unique 7 digit number combos with the first numbers all starting with 4 (matrix[1][0]). The traversal is meant to be like that of a rook on a chess board.. 1 way either horizontally or vertically...(Having 4125874 is valid 7 digit combo btw). I tried writing some code and doing regular 2D matrix traversal with a boolean visited flag here to get an answer and storing each combo in a hashSet to ensure uniqueness but I am stuck. Any kind comments, hints and code revisions to get me code working would be appreciated. class Ideone { void dfs(int[][] matrix, boolean visited) //considered dfs with a boolean visited flag but I am stuck. I want to make my solution recursive { boolean visited = false; } public static HashSet<String> get7DigitCombo(int[][] matrix) { String result = ""; int[][] cache = matrix.clone(); Set<String> comboSet = new HashSet<String>(); boolean visited = false; int resultStart = matrix[1][0]; for(int row = 1; row < matrix.length; row++) { for(int col = 0; col < matrix[0].length; col++) { if (visited == false & result.length < 7) { result += "" + (matrix[row + 1][col] || matrix[row -1][col] || matrix[row][col+1] || matrix[row][col-1]); } } } comboSet.add(result); return comboSet; } public static void main (String[] args) throws java.lang.Exception { // your code goes here int[][] matrix = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, }; HashSet<String> comboSet = get7DigitCombo(matrix); System.out.print(comboSet); } }
The following mcve demonstrates recursively getting neighbors and accumulating then into unique combinations. The code is documented with comments: import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; class Ideone { private static final int SIZE = 7; //size of combo private static int[][] directions = { //represents moving directions {-1, 0}, //up { 0,-1}, //left { 0, 1}, //right { 1, 0} //down }; public static void main (String[] args) throws java.lang.Exception { int[][] matrix = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, }; Set<String> comboSet = get7DigitCombo(matrix); System.out.print(comboSet.size()); } public static Set<String> get7DigitCombo(int[][] matrix) { Set<String> comboSet = new HashSet<>(); get7DigitCombo(1, 0, matrix, String.valueOf(matrix[1][0]), comboSet); return comboSet; } //recursively get all neighbors. generate combos by appending each neighbor //combo represents a single combination. combos accumulates combination static void get7DigitCombo(int row, int col, int[][] matrix, String combo, Set<String> combos){ if(combo !=null && combo.length() == SIZE) { //when combo reached the right size, add it //System.out.println(combo); combos.add(combo); return; } //get and iterate over all adjacent neighbors for(int[] neighbor : getNeighbors(row, col, matrix)){ get7DigitCombo(neighbor[0], neighbor[1], matrix, combo+neighbor[2], combos); } } //return list of adjacent neighbors. each neighbor is represented by //int[3]: row, column, value private static List<int[]> getNeighbors(int row, int col, int[][] matrix) { List<int[]> neighbors = new ArrayList<>(); for(int[] dir : directions){ int newRow = row + dir[0] ; int newCol = col + dir[1]; if(isValidAddress(newRow, newCol, matrix)) { neighbors.add( new int[]{newRow,newCol, matrix[newRow][newCol]}); } } return neighbors; } private static boolean isValidAddress(int row, int col, int[][] matrix) { if(row < 0 || col < 0) return false; if(row >= matrix.length || col >= matrix[row].length) return false; return true; } }
This is a pacman problem. You must look for or define the neighbors of each matrix value. Then cross the matrix fallowing the neighbors of each matrix value. It usually resolves with recursive functions. I think you code must be change from the ground using a different approach.
Determine position of number in a grid of numbers centered around 0 and increasing in spiral
I've got the following grid of numbers centered around 0 and increasing in spiral. I need an algorithm which would receive number in spiral and return x; y - numbers of moves how to get to that number from 0. For example for number 9 it would return -2; -1. For 4 it would be 1; 1. 25|26|... etc. 24| 9|10|11|12 23| 8| 1| 2|13 22| 7| 0| 3|14 21| 6| 5| 4|15 20|19|18|17|16 This spiral can be slightly changed if it would help the algorithm to be better. Use whatever language you like. I would really appreciate mathematical explanation. Thank you.
First we need to determine which cycle (distance from center) and sector (north, east, south or west) we are in. Then we can determine the exact position of the number. The first numbers in each cycle is as follows: 1, 9, 25 This is a quadratic sequence: first(n) = (2n-1)^2 = 4n^2 - 4n + 1 The inverse of this is the cycle-number: cycle(i) = floor((sqrt(i) + 1) / 2) The length of a cycle is: length(n) = first(n+1) - first(n) = 8n The sector will then be: sector(i) = floor(4 * (i - first(cycle(i))) / length(cycle(i))) Finally, to get the position, we need to extrapolate from the position of the first number in the cycle and sector. To put it all together: def first(cycle): x = 2 * cycle - 1 return x * x def cycle(index): return (isqrt(index) + 1)//2 def length(cycle): return 8 * cycle def sector(index): c = cycle(index) offset = index - first(c) n = length(c) return 4 * offset / n def position(index): c = cycle(index) s = sector(index) offset = index - first(c) - s * length(c) // 4 if s == 0: #north return -c, -c + offset + 1 if s == 1: #east return -c + offset + 1, c if s == 2: #south return c, c - offset - 1 # else, west return c - offset - 1, -c def isqrt(x): """Calculates the integer square root of a number""" if x < 0: raise ValueError('square root not defined for negative numbers') n = int(x) if n == 0: return 0 a, b = divmod(n.bit_length(), 2) x = 2**(a+b) while True: y = (x + n//x)//2 if y >= x: return x x = y Example: >>> position(9) (-2, -1) >>> position(4) (1, 1) >>> position(123456) (-176, 80)
Do you mean something like this? I did not implement any algorithm and the code can be written better but it works - that's always a start :) Just change the threshold value for whatever you wish and you'll get the result. static int threshold=14, x=0, y=0; public static void main(String[] args) { int yChange=1, xChange=1, count=0; while( !end(count) ){ for (int i = 0; i < yChange; i++) { if( end(count) )return; count++; y--; } yChange++; for (int i = 0; i < xChange; i++) { if( end(count) )return; count++; x++; } xChange++; for (int i = 0; i < yChange; i++) { if( end(count) )return; count++; y++; } yChange++; for (int i = 0; i < xChange; i++) { if( end(count) )return; count++; x--; } xChange++; } } public static boolean end(int count){ if(count<threshold){ return false; }else{ System.out.println("count: "+count+", x: "+x+", y: "+y); return true; } }
String Reduction - Programming Contest . Solution needed
I have a question which asks us to reduce the string as follows. The input is a string having only A, B or C. Output must be length of the reduced string The string can be reduced by the following rules If any 2 different letters are adjacent, these two letters can be replaced by the third letter. Eg ABA -> CA -> B . So final answer is 1 (length of reduced string) Eg ABCCCCCCC This doesn't become CCCCCCCC, as it can be reduced alternatively by ABCCCCCCC->AACCCCCC->ABCCCCC->AACCCC->ABCCC->AACC->ABC->AA as here length is 2 < (length of CCCCCCCC) How do you go about this problem? Thanks a lot! To make things clear: the question states it wants the minimum length of the reduced string. So in the second example above there are 2 solutions possible, one CCCCCCCC and the other AA. So 2 is the answer as length of AA is 2 which is smaller than the length of CCCCCCCC = 8.
The way this question is phrased, there are only three distinct possibilities: If the string has only one unique character, the length is the same as the length of the string. 2/3. If the string contains more than one unique character, the length is either 1 or 2, always (based on the layout of the characters). Edit: As a way of proof of concept here is some grammar and its extensions: I should note that although this seems to me a reasonable proof for the fact that the length will reduce to either 1 or 2, I am reasonably sure that determining which of these lengths will result is not as trivial as I originally thought ( you would still have to recurse through all options to find it out) S : A|B|C|() S : S^ where () denotes the empty string, and s^ means any combination of the previous [A,B,C,()] characters. Extended Grammar: S_1 : AS^|others S_2 : AAS^|ABS^|ACS^|others S_3 : AAAS^| AABS^ => ACS^ => BS^| AACS^ => ABS^ => CS^| ABAS^ => ACS^ => BS^| ABBS^ => CBS^ => AS^| ABCS^ => CCS^ | AAS^| ACAS^ => ABS^ => CS^| ACBS^ => AAS^ | BBS^| ACCS^ => BCS^ => AS^| The same thing will happen with extended grammars starting with B, and C (others). The interesting cases are where we have ACB and ABC (three distinct characters in sequence), these cases result in grammars that appear to lead to longer lengths however: CCS^: CCAS^|CCBS^|CCCS^| CBS^ => AS^| CAS^ => BS^| CCCS^| AAS^: AAAS^|AABS^|AACS^| ACS^ => BS^| ABS^ => CS^| AAAS^| BBS^: BBAS^|BBBS^|BBCS^| BCS^ => AS^| BAS^ => CS^| BBBS^| Recursively they only lead to longer lengths when the remaining string contains their value only. However we have to remember that this case also can be simplified, since if we got to this area with say CCCS^, then we at one point previous had ABC ( or consequently CBA ). If we look back we could have made better decisions: ABCCS^ => AACS^ => ABS^ => CS^ CBACS^ => CBBS^ => ABS^ => CS^ So in the best case at the end of the string when we make all the correct decisions we end with a remaining string of 1 character followed by 1 more character(since we are at the end). At this time if the character is the same, then we have a length of 2, if it is different, then we can reduce one last time and we end up with a length of 1.
You can generalize the result based on individual character count of string. The algo is as follows, traverse through the string and get individual char count. Lets say if a = no# of a's in given string b = no# of b's in given string c = no# of c's in given string then you can say that, the result will be, if((a == 0 && b == 0 && c == 0) || (a == 0 && b == 0 && c != 0) || (a == 0 && b != 0 && c == 0) || (a != 0 && b == 0 && c == 0)) { result = a+b+c; } else if(a != 0 && b != 0 && c != 0) { if((a%2 == 0 && b%2 == 0 && c%2 == 0) || (a%2 == 1 && b%2 == 1 && c%2 == 1)) result = 2; else result = 1; } else if((a == 0 && b != 0 && c != 0) || (a != 0 && b == 0 && c != 0) || (a != 0 && b != 0 && c == 0)) { if(a%2 == 0 && b%2 == 0 && c%2 == 0) result = 2; else result = 1; }
I'm assuming that you are looking for the length of the shortest possible string that can be obtained after reduction. A simple solution would be to explore all possibilities in a greedy manner and hope that it does not explode exponentially. I'm gonna write Python pseudocode here because that's easier to comprehend (at least for me ;)): from collections import deque def try_reduce(string): queue = deque([string]) min_length = len(string) while queue: string = queue.popleft() if len(string) < min_length: min_length = len(string) for i in xrange(len(string)-1): substring = string[i:(i+2)] if substring == "AB" or substring == "BA": queue.append(string[:i] + "C" + string[(i+2):]) elif substring == "BC" or substring == "CB": queue.append(string[:i] + "A" + string[(i+2):]) elif substring == "AC" or substring == "CA": queue.append(string[:i] + "B" + string[(i+2):]) return min_length I think the basic idea is clear: you take a queue (std::deque should be just fine), add your string into it, and then implement a simple breadth first search in the space of all possible reductions. During the search, you take the first element from the queue, take all possible substrings of it, execute all possible reductions, and push the reduced strings back to the queue. The entire space is explored when the queue becomes empty.
Let's define an automaton with the following rules (K>=0): Incoming: A B C Current: -------------------------- <empty> A B C A(2K+1) A(2K+2) AB AC A(2K+2) A(2K+3) AAB AAC AB CA CB ABC AAB BA ACB BC ABC CCA AAB AAC and all rules obtained by permutations of ABC to get the complete definition. All input strings using a single letter are irreducible. If the input string contains at least two different letters, the final states like AB or AAB can be reduced to a single letter, and the final states like ABC can be reduced to two letters. In the ABC case, we still have to prove that the input string can't be reduced to a single letter by another reduction sequence.
Compare two characters at a time and replace if both adjacent characters are not same. To get optimal solution, run once from start of the string and once from end of the string. Return the minimum value. int same(char* s){ int i=0; for(i=0;i<strlen(s)-1;i++){ if(*(s+i) == *(s+i+1)) continue; else return 0; } return 1; } int reduceb(char* s){ int ret = 0,a_sum=0,i=0; int len = strlen(s); while(1){ i=len-1; while(i>0){ if ((*(s+i)) == (*(s+i-1))){ i--; continue; } else { a_sum = (*(s+i)) + (*(s+i-1)); *(s+i-1) = SUM - a_sum; *(s+i) = '\0'; len--; } i--; } if(same(s) == 1){ return strlen(s); } } } int reducef(char* s){ int ret = 0,a_sum=0,i=0; int len = strlen(s); while(1){ i=0; while(i<len-1){ if ((*(s+i)) == (*(s+i+1))){ i++; continue; } else { a_sum = (*(s+i)) + (*(s+i+1)); *(s+i) = SUM - a_sum; int j=i+1; for(j=i+1;j<len;j++) *(s+j) = *(s+j+1); len--; } i++; } if(same(s) == 1){ return strlen(s); } } } int main(){ int n,i=0,f=0,b=0; scanf("%d",&n); int a[n]; while(i<n){ char* str = (char*)malloc(101); scanf("%s",str); char* strd = strdup(str); f = reducef(str); b = reduceb(strd); if( f > b) a[i] = b; else a[i] = f; free(str); free(strd); i++; } for(i=0;i<n;i++) printf("%d\n",a[i]); }
import java.io.*; import java.util.*; class StringSim{ public static void main(String args[]){ Scanner sc = new Scanner(System.in); StringTokenizer st = new StringTokenizer(sc.nextLine(), " "); int N = Integer.parseInt(st.nextToken()); String op = ""; for(int i=0;i<N;i++){ String str = sc.nextLine(); op = op + Count(str) + "\n"; } System.out.println(op); } public static int Count( String str){ int min = Integer.MAX_VALUE; char pre = str.charAt(0); boolean allSame = true; //System.out.println("str :" + str); if(str.length() == 1){ return 1; } int count = 1; for(int i=1;i<str.length();i++){ //System.out.println("pre: -"+ pre +"- char at "+i+" is : -"+ str.charAt(i)+"-"); if(pre != str.charAt(i)){ allSame = false; char rep = (char)(('a'+'b'+'c')-(pre+str.charAt(i))); //System.out.println("rep :" + rep); if(str.length() == 2) count = 1; else if(i==1) count = Count(rep+str.substring(2,str.length())); else if(i == str.length()-1) count = Count(str.substring(0,str.length()-2)+rep); else count = Count(str.substring(0,i-1)+rep+str.substring(i+1,str.length())); if(min>count) min=count; }else if(allSame){ count++; //System.out.println("count: " + count); } pre = str.charAt(i); } //System.out.println("min: " + min); if(allSame) return count; return min; } }
Wouldn't a good start be to count which letter you have the most of and look for ways to remove it? Keep doing this until we only have one letter. We might have it many times but as long as it is the same we do not care, we are finished. To avoid getting something like ABCCCCCCC becoming CCCCCCCC. We remove the most popular letter: -ABCCCCCCC -AACCCCCC -ABCCCCC -AACCCC -ABCCC -AACC -ABC -AA I disagree with the previous poster who states we must have a length of 1 or 2 - what happens if I enter the start string AAA?
import java.util.LinkedList; import java.util.List; import java.util.Scanner; public class Sample { private static char[] res = {'a', 'b', 'c'}; private char replacementChar(char a, char b) { for(char c : res) { if(c != a && c != b) { return c; } } throw new IllegalStateException("cannot happen. you must've mucked up the resource"); } public int processWord(String wordString) { if(wordString.length() < 2) { return wordString.length(); } String wordStringES = reduceFromEnd(reduceFromStart(wordString)); if(wordStringES.length() == 1) { return 1; } String wordStringSE = reduceFromStart(reduceFromEnd(wordString)); if(wordString.length() == 1) { return 1; } int aLen; if(isReduced(wordStringSE)) { aLen = wordStringSE.length(); } else { aLen = processWord(wordStringSE); } int bLen; if(isReduced(wordStringES)) { bLen = wordStringES.length(); } else { bLen = processWord(wordStringES); } return Math.min(aLen, bLen); } private boolean isReduced(String wordString) { int length = wordString.length(); if(length < 2) { return true; } for(int i = 1; i < length; ++i) { if(wordString.charAt(i) != wordString.charAt(i - 1)) { return false; } } return wordString.charAt(0) == wordString.charAt(length - 1); } private String reduceFromStart(String theWord) { if(theWord.length() < 2) { return theWord; } StringBuilder buffer = new StringBuilder(); char[] word = theWord.toCharArray(); char curChar = word[0]; for(int i = 1; i < word.length; ++i) { if(word[i] != curChar) { curChar = replacementChar(curChar, word[i]); if(i + 1 == word.length) { buffer.append(curChar); break; } } else { buffer.append(curChar); if(i + 1 == word.length) { buffer.append(curChar); } } } return buffer.toString(); } private String reduceFromEnd(String theString) { if(theString.length() < 2) { return theString; } StringBuilder buffer = new StringBuilder(theString); int length = buffer.length(); while(length > 1) { char a = buffer.charAt(0); char b = buffer.charAt(length - 1); if(a != b) { buffer.deleteCharAt(length - 1); buffer.deleteCharAt(0); buffer.append(replacementChar(a, b)); length -= 1; } else { break; } } return buffer.toString(); } public void go() { Scanner scanner = new Scanner(System.in); int numEntries = Integer.parseInt(scanner.nextLine()); List<Integer> counts = new LinkedList<Integer>(); for(int i = 0; i < numEntries; ++i) { counts.add((processWord(scanner.nextLine()))); } for(Integer count : counts) { System.out.println(count); } } public static void main(String[] args) { Sample solution = new Sample(); solution.go(); } }
This is greedy approach and traversing the path starts with each possible pair and checking the min length. import java.io.*; import java.util.*; class StringSim{ public static void main(String args[]){ Scanner sc = new Scanner(System.in); StringTokenizer st = new StringTokenizer(sc.nextLine(), " "); int N = Integer.parseInt(st.nextToken()); String op = ""; for(int i=0;i<N;i++){ String str = sc.nextLine(); op = op + Count(str) + "\n"; } System.out.println(op); } public static int Count( String str){ int min = Integer.MAX_VALUE; char pre = str.charAt(0); boolean allSame = true; //System.out.println("str :" + str); if(str.length() == 1){ return 1; } int count = 1; for(int i=1;i<str.length();i++){ //System.out.println("pre: -"+ pre +"- char at "+i+" is : -"+ str.charAt(i)+"-"); if(pre != str.charAt(i)){ allSame = false; char rep = (char)(('a'+'b'+'c')-(pre+str.charAt(i))); //System.out.println("rep :" + rep); if(str.length() == 2) count = 1; else if(i==1) count = Count(rep+str.substring(2,str.length())); else if(i == str.length()-1) count = Count(str.substring(0,str.length()-2)+rep); else count = Count(str.substring(0,i-1)+rep+str.substring(i+1,str.length())); if(min>count) min=count; }else if(allSame){ count++; //System.out.println("count: " + count); } pre = str.charAt(i); } //System.out.println("min: " + min); if(allSame) return count; return min; } }
Following NominSim's observations, here is probably an optimal solution that runs in linear time with O(1) space usage. Note that it is only capable of finding the length of the smallest reduction, not the reduced string itself: def reduce(string): a = string.count('a') b = string.count('b') c = string.count('c') if ([a,b,c].count(0) >= 2): return a+b+c elif (all(v % 2 == 0 for v in [a,b,c]) or all(v % 2 == 1 for v in [a,b,c])): return 2 else: return 1
There is some underlying structure that can be used to solve this problem in O(n) time. The rules given are (most of) the rules defining a mathematical group, in particular the group D_2 also sometimes known as K (for Klein's four group) or V (German for Viergruppe, four group). D_2 is a group with four elements, A, B, C, and 1 (the identity element). One of the realizations of D_2 is the set of symmetries of a rectangular box with three different sides. A, B, and C are 180 degree rotations about each of the axes, and 1 is the identity rotation (no rotation). The group table for D_2 is |1 A B C -+------- 1|1 A B C A|A 1 C B B|B C 1 A C|C B A 1 As you can see, the rules correspond to the rules given in the problem, except that the rules involving 1 aren't present in the problem. Since D_2 is a group, it satisfies a number of rules: closure (the product of any two elements of the group is another element), associativity (meaning (x*y)*z = x*(y*z) for any elements x, y, z; i.e., the order in which strings are reduced doesn't matter), existence of identity (there is an element 1 such that 1*x=x*1=x for any x), and existence of inverse (for any element x, there is an element x^{-1} such that x*x^{-1}=1 and x^{-1}*x=1; in our case, every element is its own inverse). It's also worth noting that D_2 is commutative, i.e., x*y=y*x for any x,y. Given any string of elements in D_2, we can reduce to a single element in the group in a greedy fashion. For example, ABCCCCCCC=CCCCCCCC=CCCCCC=CCCC=CC=1. Note that we don't write the element 1 unless it's the only element in the string. Associativity tells us that the order of the operations doesn't matter, e.g., we could have worked from right to left or started in the middle and gotten the same result. Let's try from the right: ABCCCCCCC=ABCCCCC=ABCCC=ABC=AA=1. The situation of the problem is different because operations involving 1 are not allowed, so we can't just eliminate pairs AA, BB, or CC. However, the situation is not that different. Consider the string ABB. We can't write ABB=A in this case. However, we can eliminate BB in two steps using A: ABB=CB=A. Since order of operation doesn't matter by associativity, we're guaranteed to get the same result. So we can't go straight from ABB to A but we can get the same result by another route. Such alternate routes are available whenever there are at least two different elements in a string. In particular, in each of ABB, ACC, BAA, BCC, CAA, CBB, AAB, AAC, BBA, BBC, CCA, CCB, we can act as if we have the reduction xx=1 and then drop the 1. It follows that any string that is not homogeneous (not all the same letter) and has a double-letter substring (AA, BB, or CC) can be reduced by removing the double letter. Strings that contain just two identical letters can't be further reduced (because there is no 1 allowed in the problem), so it seems safe to hypothesize that any non-homogeneous string can be reduced to A, B, C, AA, BB, CC. We still have to be careful, however, because CCAACC could be turned into CCCC by removing the middle pair AA, but that is not the best we can do: CCAACC=AACC=CC or AA takes us down to a string of length 2. Another situation we have to be careful of is AABBBB. Here we could eliminate AA to end with BBBB, but it's better to eliminate the middle B's first, then whatever: AABBBB=AABB=AA or BB (both of which are equivalent to 1 in the group, but can't be further reduced in the problem). There's another interesting situation we could have: AAAABBBB. Blindly eliminating pairs takes us to either AAAA or BBBB, but we could do better: AAAABBBB=AAACBBB=AABBBB=AABB=AA or BB. The above indicate that eliminating doubles blindly is not necessarily the way to proceed, but nevertheless it was illuminating. Instead, it seems as if the most important property of a string is non-homogeneity. If the string is homogeneous, stop, there's nothing we can do. Otherwise, identify an operation that preserves the non-homogeneity property if possible. I assert that it is always possible to identify an operation that preserves non-homogeneity if the string is non-homogeneous and of length four or greater. Proof: if a 4-substring contains two different letters, a third letter can be introduced at a boundary between two different letters, e.g., AABA goes to ACA. Since one or the other of the original letters must be unchanged somewhere within the string, it follows that the result is still non-homogeneous. Suppose instead we have a 4-substring that has three different elements, say AABC, with the outer two elements different. Then if the middle two elements are different, perform the operation on them; the result is non-homogeneous because the two outermost elements are still different. On the other hand, if the two inner elements are the same, e.g., ABBC, then they have to be different from both outermost elements (otherwise we'd only have two elements in the set of four, not three). In that case, perform either the first or third operation; that leaves either the last two elements different (e.g., ABBC=CBC) or the first two elements different (e.g., ABBC=ABA) so non-homogeneity is preserved. Finally, consider the case where the first and last elements are the same. Then we have a situation like ABCA. The middle two elements both have to be different from the outer elements, otherwise we'd have only two elements in this case, not three. We can take the first available operation, ABCA=CCA, and non-homogeneity is preserved again. End of proof. We have a greedy algorithm to reduce any non-homogeneous string of length 4 or greater: pick the first operation that preserves non-homogeneity; such an operation must exist by the above argument. We have now reduced to the case where we have a non-homogeneous string of 3 elements. If two are the same, we either have doubles like AAB etc., which we know can be reduced to a single element, or we have two elements with no double like ABA=AC=B which can also be reduced to a single element, or we have three different elements like ABC. There are six permutations, all of which =1 in the group by associativity and commutativity; all of them can be reduced to two elements by any operation; however, they can't possibly be reduced below a homogeneous pair (AA, BB, or CC) since 1 is not allowed in the problem, so we know that's the best we can do in this case. In summary, if a string is homogeneous, there's nothing we can do; if a string is non-homogeneous and =A in the group, it can be reduced to A in the problem by a greedy algorithm which maintains non-homogeneity at each step; the same if the string =B or =C in the group; finally if a string is non-homogeneous and =1 in the group, it can be reduced by a greedy algorithm which maintains non-homogeneity as long as possible to one of AA, BB or CC. Those are the best we can do by the group properties of the operation. Program solving the problem: Now, since we know the possible outcomes, our program can run in O(n) time as follows: if all the letters in the given string are the same, no reduction is possible so just output the length of the string. If the string is non-homogeneous, and is equal to the identity in the group, output the number 2; otherwise output the number 1. To quickly decide whether an element equals the identity in the group, we use commutativity and associativity as follows: just count the number of A's, B's and C's into the variables a, b, c. Replace a = a mod 2, b = b mod 2, c = c mod 2 because we can eliminate pairs AA, BB, and CC in the group. If none of the resulting a, b, c is equal to 0, we have ABC=1 in the group, so the program should output 2 because a reduction to the identity 1 is not possible. If all three of the resulting a, b, c are equal to 0, we again have the identity (A, B, and C all cancelled themselves out) so we should output 2. Otherwise the string is non-identity and we should output 1.
//C# Coding using System; using System.Collections.Generic; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { /* Keep all the rules in Dictionary object 'rules'; key - find string, value - replace with value eg: find "AB" , replace with "AA" */ Dictionary<string, string> rules = new Dictionary<string, string>(); rules.Add("AB", "AA"); rules.Add("BA", "AA"); rules.Add("CB", "CC"); rules.Add("BC", "CC"); rules.Add("AA", "A"); rules.Add("CC", "C"); // example string string str = "AABBCCCA"; //output Console.WriteLine(fnRecurence(rules, str)); Console.Read(); } //funcation for applying all the rules to the input string value recursivily static string fnRecurence(Dictionary<string, string> rules,string str) { foreach (var rule in rules) { if (str.LastIndexOf(rule.Key) >= 0) { str = str.Replace(rule.Key, rule.Value); } } if(str.Length >1) { int find = 0; foreach (var rule in rules) { if (str.LastIndexOf(rule.Key) >= 0) { find = 1; } } if(find == 1) { str = fnRecurence(rules, str); } else { //if not find any exit find = 0; str = str; return str; } } return str; } } }
Here is my C# solution. public static int StringReduction(string str) { if (str.Length == 1) return 1; else { int prevAns = str.Length; int newAns = 0; while (prevAns != newAns) { prevAns = newAns; string ansStr = string.Empty; int i = 1; int j = 0; while (i < str.Length) { if (str[i] != str[j]) { if (str[i] != 'a' && str[j] != 'a') { ansStr += 'a'; } else if (str[i] != 'b' && str[j] != 'b') { ansStr += 'b'; } else if (str[i] != 'c' && str[j] != 'c') { ansStr += 'c'; } i += 2; j += 2; } else { ansStr += str[j]; i++; j++; } } if (j < str.Length) { ansStr += str[j]; } str = ansStr; newAns = ansStr.Length; } return newAns; } }
Compare two characters at a time and replace if both adjacent characters are not same. To get optimal solution, run once from start of the string and once from end of the string. Return the minimum value. Rav solution is :- int same(char* s){ int i=0; for(i=0;i<strlen(s)-1;i++){ if(*(s+i) == *(s+i+1)) continue; else return 0; } return 1; } int reduceb(char* s){ int ret = 0,a_sum=0,i=0; int len = strlen(s); while(1){ i=len-1; while(i>0){ if ((*(s+i)) == (*(s+i-1))){ i--; continue; } else { a_sum = (*(s+i)) + (*(s+i-1)); *(s+i-1) = SUM - a_sum; *(s+i) = '\0'; len--; } i--; } if(same(s) == 1){ return strlen(s); } } } int reducef(char* s){ int ret = 0,a_sum=0,i=0; int len = strlen(s); while(1){ i=0; while(i<len-1){ if ((*(s+i)) == (*(s+i+1))){ i++; continue; } else { a_sum = (*(s+i)) + (*(s+i+1)); *(s+i) = SUM - a_sum; int j=i+1; for(j=i+1;j<len;j++) *(s+j) = *(s+j+1); len--; } i++; } if(same(s) == 1){ return strlen(s); } } } int main(){ int n,i=0,f=0,b=0; scanf("%d",&n); int a[n]; while(i<n){ char* str = (char*)malloc(101); scanf("%s",str); char* strd = strdup(str); f = reducef(str); b = reduceb(strd); if( f > b) a[i] = b; else a[i] = f; free(str); free(strd); i++; } for(i=0;i<n;i++) printf("%d\n",a[i]); } #Rav this code will fail for input "abccaccba". solution should be only "b" but this code wont give that. Since i am not getting correct comment place(due to low points or any other reason) so i did it here.
This problem can be solved by greedy approach. Try to find the best position to apply transformation until no transformation exists. The best position is the position with max number of distinct neighbors of the transformed character.
You can solve this using 2 pass. In the first pass you apply len = strlen (str) ; index = 0 ; flag = 0 ; /* 1st pass */ for ( i = len-1 ; i > 0 ; i -- ) { if ( str[i] != str[i-1] ) { str[i-1] = getChar (str[i], str[i-1]) ; if (i == 1) { output1[index++] = str[i-1] ; flag = 1 ; break ; } } else output1[index++] = str[i] ; } if ( flag == 0 ) output1[index++] = str[i] ; output1[index] = '\0'; And in the 2nd pass you will apply the same on 'output1' to get the result. So, One is forward pass another one is backward pass.
int previous = a.charAt(0); boolean same = true; int c = 0; for(int i = 0; i < a.length();++i){ c ^= a.charAt(i)-'a'+1; if(a.charAt(i) != previous) same = false; } if(same) return a.length(); if(c==0) return 2; else return 1;
import java.util.Scanner; public class StringReduction { public static void main(String[] args) { Scanner sc = new Scanner(System.in); String str = sc.nextLine(); int length = str.length(); String result = stringReduction(str); System.out.println(result); } private static String stringReduction(String str) { String result = str.substring(0); if(str.length()<2){ return str; } if(str.length() == 2){ return combine(str.charAt(0),str.charAt(1)); } for(int i =1;i<str.length();i++){ if(str.charAt(i-1) != str.charAt(i)){ String temp = str.substring(0, i-1) + combine(str.charAt(i-1),str.charAt(i)) + str.substring(i+1, str.length()); String sub = stringReduction(temp); if(sub.length() < result.length()){ result = sub; } } } return result; } private static String combine(char c1, char c2) { if(c1 == c2){ return "" + c1 + c2; } else{ if(c1 == 'a'){ if(c2 == 'b'){ return "" + 'c'; } if(c2 == 'c') { return "" + 'b'; } } if(c1 == 'b'){ if(c2 == 'a'){ return "" + 'c'; } if(c2 == 'c') { return "" + 'a'; } } if(c1 == 'c'){ if(c2 == 'a'){ return "" + 'b'; } if(c2 == 'b') { return "" + 'a'; } } return null; } } }
JAVASCRIPT SOLUTION: function StringChallenge(str) { // code goes here if(str.length == 1) { return 1; } else { let prevAns = str.length; let newAns = 0; while(prevAns != newAns) { prevAns = newAns; let ansStr = ""; let i = 1; let j = 0; while(i < str.length) { if(str[i] !== str[j]) { if(str[i] != 'a' && str[j] != 'a') { ansStr += 'a'; } else if(str[i] != 'b' && str[j] !='b') { ansStr +='b'; } else if(str[i] != 'c' && str[j] != 'c') { ansStr += 'c'; } i += 2; j += 2; } else { ansStr += str[j]; j++; i++; } } if(j < str.length) { ansStr += str[j]; } str = ansStr; newAns = ansStr.length; } return newAns; } }
Formula for the max number of paths through a grid?
Given a grid of open spots, and a certain number of tiles to place in those spots, what function f(openSpots, tilesToPlace) will give you the number of continuous paths you can form? Continuous paths are placements of the tiles such that each tile shares an edge with another. (Only corners touching is not good enough. So (0, 1) and (0, 0) are legal, but (1, 1) and (2, 2) is not.) I already have a function that will find all these paths. However, it only works for small numbers. For larger values, all I need is a count of how many could possibly exist. Here is some data: For 1 tiles, there are 1 paths. For 2 tiles, there are 4 paths. For 3 tiles, there are 22 paths. For 4 tiles, there are 89 paths. For 5 tiles, there are 390 paths. For 6 tiles, there are 1476 paths. For 7 tiles, there are 5616 paths. For 8 tiles, there are 19734 paths. For 9 tiles, there are 69555 paths. This gets really slow to calculate as the puzzle size increases. I think the asymptotic complexity of my path finding solution is pretty bad. If there are n tiles, the grid is at most n spots long and wide.
Your problem seems to be at least as difficult as enumerating polyominoes. There are no known fast algorithms for doing this, and the best known algorithms struggle after n=50. I doubt there is a fast way to solve this problem. I'm not even going to pretend that this is an optimal solution but it might be useful as a reference solution. I think it at least gives the correct answer, although it takes some time. It solves the problem recursively by finding all paths of length n-1, then checking for all possible places it can add one more tile and removing duplicate solutions. It has a particularly ugly part where it checks for duplicate by converting the path to a string and comparing the strings, but it was fast to write. Here's the output it generates: n = 1, number of paths found = 1 n = 2, number of paths found = 4 n = 3, number of paths found = 22 n = 4, number of paths found = 113 n = 5, number of paths found = 571 n = 6, number of paths found = 2816 n = 7, number of paths found = 13616 n = 8, number of paths found = 64678 n = 9, number of paths found = 302574 And here's the code: using System; using System.Collections.Generic; using System.Linq; public struct Tile { public Tile(int x, int y) { X = x; Y = y; } public readonly int X; public readonly int Y; public IEnumerable<Tile> GetNeighbours(int gridSize) { if (X > 0) yield return new Tile(X - 1, Y); if (X < gridSize - 1) yield return new Tile(X + 1, Y); if (Y > 0) yield return new Tile(X, Y - 1); if (Y < gridSize - 1) yield return new Tile(X, Y + 1); } public override string ToString() { return string.Format("({0},{1})", X, Y); } } public class Path { public Path(Tile[] tiles) { Tiles = tiles; } public Tile[] Tiles { get; private set; } public override string ToString() { return string.Join("", Tiles.Select(tile => tile.ToString()).ToArray()); } } public class PathFinder { public IEnumerable<Path> FindPaths(int n, int gridSize) { if (n == 1) { for (int x = 0; x < gridSize; ++x) for (int y = 0; y < gridSize; ++y) yield return new Path(new Tile[] { new Tile(x, y) }); } else { Dictionary<string, object> pathsSeen = new Dictionary<string, object>(); foreach (Path shortPath in FindPaths(n - 1, gridSize)) { foreach (Tile tile in shortPath.Tiles) { foreach (Tile neighbour in tile.GetNeighbours(gridSize)) { // Ignore tiles that are already included in the path. if (shortPath.Tiles.Contains(neighbour)) continue; Path newPath = new Path(shortPath.Tiles .Concat(new Tile[] { neighbour }) .OrderBy(t => t.X) .ThenBy(t => t.Y) .ToArray()); string pathKey = newPath.ToString(); if (!pathsSeen.ContainsKey(pathKey)) { pathsSeen[pathKey] = null; yield return newPath; } } } } } } static void Main() { PathFinder pathFinder = new PathFinder(); for (int n = 1; n <= 9; ++n) { List<Path> paths = pathFinder.FindPaths(n, n).ToList(); Console.WriteLine("n = {0}, number of paths found = {1}", n, paths.Count); //foreach (Path path in paths) // Console.WriteLine(path.ToString()); } } }
Converting a decimal to a mixed-radix (base) number
How do you convert a decimal number to mixed radix notation? I guess that given an input of an array of each of the bases, and the decimal number, it should output an array of the values of each column.
Pseudocode: bases = [24, 60, 60] input = 86462 #One day, 1 minute, 2 seconds output = [] for base in reverse(bases) output.prepend(input mod base) input = input div base #div is integer division (round down)
Number -> set: factors = [52,7,24,60,60,1000] value = 662321 for i in n-1..0 res[i] = value mod factors[i] value = value div factors[i] And the reverse: If you have the number like 32(52), 5(7), 7(24), 45(60), 15(60), 500(1000) and you want this converted to decimal: Take number n, multiply it with the factor of n-1, continue for n-1..n=0 values = [32,5,7,45,15,500] factors = [52,7,24,60,60,1000] res = 0; for i in 0..n-1 res = res * factors[i] + values[i] And you have the number.
In Java you could do public static int[] Number2MixedRadix(int[] base, int number) throws Exception { //NB if the max number you want # a position is say 3 then the base# tha position //in your base array should be 4 not 3 int[] RadixFigures = new int[base.length]; int[] PositionPowers = new int[base.length]; PositionPowers[base.length-1] = 1; for (int k = base.length-2,pow = 1; k >-1; k--){ pow*=base[k+1]; PositionPowers[k]=pow; }for (int k = 0; k<base.length; k++){ RadixFigures[k]=number/PositionPowers[k]; if(RadixFigures[k]>base[k])throw new Exception("RadixFigure#["+k+"] => ("+RadixFigures[k]+") is > base#["+k+"] => ("+base[k]+") | ( number is Illegal )"); number=number%PositionPowers[k]; }return RadixFigures; } Example //e.g. mixed-radix base for 1day int[] base = new int[]{1, 24, 60, 60};//max-day,max-hours,max-minutes,max-seconds int[] MixedRadix = Number2MixedRadix(base, 19263);//19263 seconds //this would give [0,5,21,3] => as per 0days 5hrs 21mins 3secs Reversal public static int MixedRadix2Number(int[] RadixFigures,int[] base) throws Exception { if(RadixFigures.length!=base.length)throw new Exception("RadixFigures.length must be = base.length"); int number=0; int[] PositionPowers = new int[base.length]; PositionPowers[base.length-1] = 1; for (int k = base.length-2,pow = 1; k >-1; k--){ pow*=base[k+1]; PositionPowers[k]=pow; }for (int k = 0; k<base.length; k++){ number+=(RadixFigures[k]*PositionPowers[k]); if(RadixFigures[k]>base[k])throw new Exception("RadixFigure#["+k+"] => ("+RadixFigures[k]+") is > base#["+k+"] => ("+base[k]+") | ( number is Illegal )"); }return number; }
I came up with a slightly different, and probably not as good method as the other ones here, but I thought I'd share anyway: var theNumber = 313732097; // ms s m h d var bases = [1000, 60, 60, 24, 365]; var placeValues = []; // initialise an array var currPlaceValue = 1; for (var i = 0, l = bases.length; i < l; ++i) { placeValues.push(currPlaceValue); currPlaceValue *= bases[i]; } console.log(placeValues); // this isn't relevant for this specific problem, but might // be useful in related problems. var maxNumber = currPlaceValue - 1; var output = new Array(placeValues.length); for (var v = placeValues.length - 1; v >= 0; --v) { output[v] = Math.floor(theNumber / placeValues[v]); theNumber %= placeValues[v]; } console.log(output); // [97, 52, 8, 15, 3] --> 3 days, 15 hours, 8 minutes, 52 seconds, 97 milliseconds
I tried a few of the examples before and found an edge case they didn't cover, if you max out your scale you need to prepend the result from the last step def intToMix(number,radix=[10]): mixNum=[] radix.reverse() for i in range(0,len(radix)): mixNum.append(number%radix[i]) number//=radix[i] mixNum.append(number) mixNum.reverse() radix.reverse() return mixNum num=60*60*24*7 radix=[7,24,60,60] tmp1=intToMix(num,radix)