ばかげた質問のように思えますが、任意のコレクションでの size() の時間の複雑さは O(1) になると予想されますが、サイズの呼び出しを必要とするコードの「最適化」が見つかりました() は実際に速度を落としています。
では、Java の Sets の size() の時間計算量はどのくらいですか?
私のコードは、グラフ内の最大クリークを見つけるための再帰アルゴリズムの実装です (重要ではありません)。基本的に、最適化では、2 つの Set のサイズが等しいかどうか (これらの Set はいずれかの方法で構築されているかどうか) を確認し、サイズが等しい場合は、あと 1 回だけ再帰呼び出しを許可します (その後、再帰を停止します)。
これが私のコードの(簡略化された)バージョンです:
private static void recursivelyFindMaximalCliques(Set<Integer> subGraph, Set<Integer> candidates, Set<Integer> notCandidates) {
boolean noFurtherCliques = false;
Iterator<Integer> candidateIterator = candidates.iterator();
while (candidateIterator.hasNext()) {
int nextCandidate = candidateIterator.next();
candidateIterator.remove();
subGraph.add(nextCandidate);
Set<Integer> neighbors = getNeighbors(nextCandidate);
Set<Integer> newCandidates = calculateIntersection(candidates, neighbors);
Set<Integer> newNotCandidates = calculateIntersection(notCandidates, neighbors);
//if (newCandidates.size() == candidates.size())
// noFurtherCliques = true;
recursivelyFindMaximalCliques(subGraph, newCandidates, newNotCandidates);
//if (noFurtherCliques)
// return;
subGraph.set.remove(nextCandidate);
notCandidates.set.add(nextCandidate);
}
}
私がコメントアウトした行が問題の行です。newCandidates と候補のセットが同じサイズであるかどうかを確認し、同じサイズの場合、再帰は 1 レベル深くまでしか許可されていません。
行のコメントを外すと、コードの実行速度が約 10% 遅くなります。これは、使用されるセットが HashSet、TreeSet、または LinkedHashSet のいずれであっても当てはまります。これらの行により、再帰呼び出しが少なくなることが保証されるため、これは意味がありません。
私が推測できる唯一のことは、セットでの size() の呼び出しに時間がかかっていることです。Javaのセットでsize()を呼び出すと、O(1)より時間がかかりますか?
編集
何人かが尋ねたので、ここに calculateIntersection() があります:
private static IntegerSet calculateIntersection(Set<Integer> setA, Set<Integer> setB) {
if (setA.size() == 0 || setB.size() == 0)
return new Set<Integer>();
Set<Integer> intersection = new Set<Integer>(); //Replace this with TreeSet, HashSet, or LinkedHashSet, whichever is being used
intersection.addAll(setA);
intersection.retainAll(setB);
return intersection;
}
2番目の編集 必要に応じて、完全なコードを次に示します。長くて厄介なので、投稿するのをためらっていましたが、人々から尋ねられたので、ここに掲載します。
public class CliqueFindingAlgorithm {
private static class IntegerSet {
public Set<Integer> set = new TreeSet<Integer>(); //Or whatever Set is being used
}
private static ArrayList<IntegerSet> findMaximalCliques(UndirectedGraph graph) {
ArrayList<IntegerSet> cliques = new ArrayList<IntegerSet>();
IntegerSet subGraph = new IntegerSet();
IntegerSet candidates = new IntegerSet();
IntegerSet notCandidates = new IntegerSet();
for (int vertex = 0; vertex < graph.getNumVertices(); vertex++) {
candidates.set.add(vertex);
}
recursivelyFindMaximalCliques(cliques, graph, subGraph, candidates, notCandidates);
return cliques;
}
private static void recursivelyFindMaximalCliques(ArrayList<IntegerSet> cliques, UndirectedGraph graph,
IntegerSet subGraph, IntegerSet candidates, IntegerSet notCandidates) {
boolean noFurtherCliques = false;
Iterator<Integer> candidateIterator = candidates.set.iterator();
while (candidateIterator.hasNext()) {
int nextCandidate = candidateIterator.next();
candidateIterator.remove();
subGraph.set.add(nextCandidate);
IntegerSet neighbors = new IntegerSet();
neighbors.set = graph.getNeighbors(nextCandidate);
IntegerSet newCandidates = calculateIntersection(candidates, neighbors);
IntegerSet newNotCandidates = calculateIntersection(notCandidates, neighbors);
if (newCandidates.set.size() == candidates.set.size())
noFurtherCliques = true;
recursivelyFindMaximalCliques(cliques, graph, subGraph, newCandidates, newNotCandidates);
if (noFurtherCliques)
return;
subGraph.set.remove(nextCandidate);
notCandidates.set.add(nextCandidate);
}
if (notCandidates.set.isEmpty()) {
IntegerSet clique = new IntegerSet();
clique.set.addAll(subGraph.set);
cliques.add(clique);
}
}
private static IntegerSet calculateIntersection(IntegerSet setA, IntegerSet setB) {
if (setA.set.size() == 0 || setB.set.size() == 0)
return new IntegerSet();
IntegerSet intersection = new IntegerSet();
intersection.set.addAll(setA.set);
intersection.set.retainAll(setB.set);
return intersection;
}
}
public class UndirectedGraph {
// ------------------------------ PRIVATE VARIABLES ------------------------------
private ArrayList<TreeMap<Integer, Double>> neighborLists;
private int numEdges;
// ------------------------------ CONSTANTS ------------------------------
// ------------------------------ CONSTRUCTORS ------------------------------
public UndirectedGraph(int numVertices) {
this.neighborLists = new ArrayList<TreeMap<Integer, Double>>(numVertices);
this.numEdges = 0;
for (int i = 0; i < numVertices; i++) {
this.neighborLists.add(new TreeMap<Integer, Double>());
}
}
// ------------------------------ PUBLIC METHODS ------------------------------
public void addEdge(int vertexA, int vertexB, double edgeWeight) {
TreeMap<Integer, Double> vertexANeighbors = this.neighborLists.get(vertexA);
TreeMap<Integer, Double> vertexBNeighbors = this.neighborLists.get(vertexB);
vertexANeighbors.put(vertexB, edgeWeight);
vertexBNeighbors.put(vertexA, edgeWeight);
this.numEdges++;
}
public List<Integer> computeCommonNeighbors(int vertexA, int vertexB) {
List<Integer> commonNeighbors = new ArrayList<Integer>();
Iterator<Integer> iteratorA = this.getNeighbors(vertexA).iterator();
Iterator<Integer> iteratorB = this.getNeighbors(vertexB).iterator();
if (iteratorA.hasNext() && iteratorB.hasNext()) {
int nextNeighborA = iteratorA.next();
int nextNeighborB = iteratorB.next();
while(true) {
if (nextNeighborA == nextNeighborB) {
commonNeighbors.add(nextNeighborA);
if (iteratorA.hasNext() && iteratorB.hasNext()) {
nextNeighborA = iteratorA.next();
nextNeighborB = iteratorB.next();
}
else
break;
}
else if (nextNeighborA < nextNeighborB) {
if (iteratorA.hasNext())
nextNeighborA = iteratorA.next();
else
break;
}
else if (nextNeighborB < nextNeighborA) {
if (iteratorB.hasNext())
nextNeighborB = iteratorB.next();
else
break;
}
}
}
return commonNeighbors;
}
// ------------------------------ PRIVATE METHODS ------------------------------
private class EdgeIterator implements Iterator<int[]> {
private int vertex;
private int[] nextPair;
private Iterator<Integer> neighborIterator;
public EdgeIterator() {
this.vertex = 0;
this.neighborIterator = neighborLists.get(0).keySet().iterator();
this.getNextPair();
}
public boolean hasNext() {
return this.nextPair != null;
}
public int[] next() {
if (this.nextPair == null)
throw new NoSuchElementException();
int[] temp = this.nextPair;
this.getNextPair();
return temp;
}
public void remove() {
throw new UnsupportedOperationException();
}
private void getNextPair() {
this.nextPair = null;
while (this.nextPair == null && this.neighborIterator != null) {
while (this.neighborIterator.hasNext()) {
int neighbor = this.neighborIterator.next();
if (this.vertex <= neighbor) {
this.nextPair = new int[] {vertex, neighbor};
return;
}
}
this.vertex++;
if (this.vertex < getNumVertices())
this.neighborIterator = neighborLists.get(this.vertex).keySet().iterator();
else
this.neighborIterator = null;
}
}
}
// ------------------------------ GETTERS & SETTERS ------------------------------
public int getNumEdges() {
return this.numEdges;
}
public int getNumVertices() {
return this.neighborLists.size();
}
public Double getEdgeWeight(int vertexA, int vertexB) {
return this.neighborLists.get(vertexA).get(vertexB);
}
public Set<Integer> getNeighbors(int vertex) {
return Collections.unmodifiableSet(this.neighborLists.get(vertex).keySet());
}
public Iterator<int[]> getEdgeIterator() {
return new EdgeIterator();
}
}