5

ばかげた質問のように思えますが、任意のコレクションでの 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();
}

}

4

2 に答える 2

7

実装によって異なります。たとえば、変数を返す内部をHashSet.size()呼び出すだけです。size()hashMap

//HashSet
public int size() {
    return map.size();
}

//Hashmap
public int size() {
    return size;
}
于 2013-05-22T03:12:54.083 に答える
4

実装に依存します。たとえば、 を考えてみましょうSortedSet.subSetSortedSet<E>これは a (したがって a ) を返しますが、そのサブセットに対する操作が O(1) であるSet<E>とは期待していません。size()

使用しているセットの種類や、メソッドが正確に何をするかについては言及していませんがcalculateIntersection、既存のセットにビューを返す場合、そのビューのサイズを見つけることが高い。

HashSetTreeSet、およびについて話しましたが、これらLinkedHashSetすべてO(1) for ... ですが、メソッドがもともとこれらのセットの 1 つを取り、そこからビューを作成する場合、何が起こっているのかを十分に説明できます。詳細を教えていただけると助かります。理想的には、問題を再現するために使用できる短いが完全なプログラムです。size()calculateIntersection

于 2013-05-22T03:10:58.187 に答える