2

Javaでは、最大値を見つけるためのアルゴリズムが必要です。整数のコレクション内の出現回数。たとえば、私のセットが[2,4,3,2,2,1,4,2,2]である場合、2はほとんど発生する整数であり、5回出現するため、アルゴリズムは5を出力する必要があります。整数のセットのヒストグラムのピークを見つけるようなものと考えてください。

課題は、多くの整数の複数のセットに対して1つずつ実行する必要があるため、効率的である必要があるということです。また、どの要素が主にセットに表示されるかはわかりません。それは完全にランダムです。

セットのそれらの値を配列に入れ、それをソートしてから配列を反復処理し、数字の連続した出現をカウントし、カウントの最大値を特定することを考えましたが、かなりの時間がかかると思います。それを効率的に行うのに役立つライブラリやアルゴリズムはありますか?

4

7 に答える 7

3

並べ替えの何が問題になっていますか?それはO(n log n)であり、まったく悪くはありません。より良い解決策は、入力セットに関するより多くの情報(おそらく、関係する数の上限)を必要とするか、Map<Integer, Integer>または同等のものを含むでしょう。

于 2012-05-21T01:37:35.857 に答える
3

次のロジックを使用して、コレクションをループしてMapデータ構造に挿入します。

  • 整数がまだマップに挿入されていない場合は、key = integer、value=1を挿入します。
  • キーが存在する場合は、値をインクリメントします。

Javaには、HashMapとTreeMapの2つのマップがあります。これらを以下で比較します。

HashMapとTreeMap

必要に応じて、詳細な説明をスキップして、要約に直接ジャンプできます。

HashMapは、キーと値のペアを配列に格納するマップです。キーkに使用されるインデックスは次のとおりです。

  • h.hashCode()%map.size()

2つの完全に異なるキーが同じインデックスになる場合があります。これを解決するために、配列内の各場所は実際にはリンクリストです。つまり、すべてのルックアップは常にリンクリストをループし、k.equals(other)メソッドを使用して等しいかどうかをチェックする必要があります。最悪の場合、すべてのキーが同じ場所に保存され、HashMapはインデックス付けされていないリストになります。

HashMapがより多くのエントリを取得すると、これらの衝突の可能性が高まり、構造の効率が低下します。これを解決するために、エントリの数がクリティカルポイント(コンストラクターのloadFactor引数によって決定される)に達すると、構造のサイズが変更されます。

  • 新しいアレイは、現在のサイズの約2倍で割り当てられます
  • ループは既存のすべてのキーに対して実行されます
    • キーの場所が新しい配列に対して再計算されます
    • キーと値のペアが新しい構造に挿入されます

ご覧のとおり、サイズ変更が多い場合、これは比較的コストがかかる可能性があります。

この問題は、開始する前に適切なサイズでHashMapを事前に割り当てることができれば、克服できます。たとえば、map = new HashMap(input.size()* 1.5)です。大規模なデータセットの場合、これによりメモリチャーンを大幅に減らすことができます。

キーは基本的にHashMap内でランダムに配置されるため、キーイテレーターはランダムな順序でキーを反復処理します。Javaは、キーが挿入された順序で反復するLinkedHashMapを提供します。

HashMapのパフォーマンス:

  • ハッシュの正しいサイズと適切な分布を考えると、ルックアップは一定時間です。
  • 分布が悪いと、パフォーマンスは(最悪の場合)線形探索-O(n)に低下します。
  • 初期サイズが悪いと、パフォーマンスは再ハッシュのパフォーマンスになります。これを簡単に計算することはできませんが、良くありません。

OTOH a TreeMapは、エントリをバランスの取れたツリーに格納します。動的構造は、キーと値のペアが追加されるにつれて段階的に構築されます。挿入はツリーの深さ(log(tree.size())に依存しますが、予測可能です。HashMapとは異なり、中断やパフォーマンスが低下するエッジ条件はありません。

十分に分散されたHashMapを考えると、各挿入とルックアップはより高価です。

さらに、キーをツリーに挿入するには、すべてのキーが他のすべてのキーと比較可能である必要があり、Comparableインターフェイスのk.compare(other)メソッドが必要です。明らかに、質問が整数に関するものであることを考えると、これは問題ではありません。

TreeMapのパフォーマンス:

  • n個の要素の挿入はO(n log n)です
  • ルックアップはO(log n)です

概要

最初の考え:データセットのサイズ:

  • 小さい場合(1000年代と10,000年代でも)、最新のハードウェアでは実際には問題になりません。
  • マシンがメモリ不足になるほど大きい場合は、TreeMapが唯一のオプションである可能性があります
  • そうでなければ、サイズはおそらく決定要因ではありません

この特定のケースでは、重要な要素は、データセット全体のサイズと比較して、予想される一意の整数の数が多いか少ないかです。

  • 小さい場合、全体の時間は小さいセットでのキールックアップによって支配されるため、最適化は関係ありません(ここで停止できます)。
  • 大きい場合、全体の時間は挿入によって支配され、決定はより多くの要因に依存します。
    • データセットのサイズは既知ですか?
      • はいの場合:HashMapを事前に割り当てることができるため、メモリチャーンが排除されます。これは、hashCode()メソッドが高価な場合(この場合ではない)に特に重要です。
      • いいえの場合:TreeMapはより予測可能なパフォーマンスを提供し、より適切な選択となる可能性があります
    • リアルタイムシステムやGUIのイベントスレッドなど、大きなストールを必要としない予測可能なパフォーマンスはありますか?
      • はいの場合:TreeMapは、ストールなしではるかに優れた予測可能性を提供します
      • いいえの場合:HashMapは、おそらく計算全体の全体的なパフォーマンスを向上させます

上からの圧倒的なポイントがない場合の最後のポイント:

  • ソートされた値のキーのリストはありますか?
    • はいの場合(たとえば、ヒストグラムを印刷する場合):TreeMapはすでにキーをソートしているため、便利です。

ただし、パフォーマンスが重要な場合、決定する唯一の方法は、Mapインターフェースに実装し、HashMapとTreeMapの両方をプロファイリングして、どちらが実際に状況に適しているかを確認することです時期尚早の最適化は多くの悪の根源です:)

于 2012-05-21T01:40:28.490 に答える
2
  1. 基本的な方法は、コレクションを並べ替えてから、並べ替えられたコレクションを実行することです。(これは、O(nLog(n))であるO(nLog(n)+ n)で実行されます)。

  2. 数値が制限されていて(たとえば、-10000,10000)、コレクションに多数の整数が含まれている場合は、ルックアップテーブルを使用して、各要素をカウントできます。これには、O(n + l)(カウントにはO(n)、最大要素を見つけるにはO(l))が必要です。ここで、lは範囲の長さ(この場合は20001)です。ご覧のとおり、n >> lの場合、これは1よりも優れたO(n)になりますが、n << lの場合、定数ですが、これを使用できなくするのに十分な大きさのO(l)になります。

  3. 前の別の変形は、ルックアップテーブルの代わりにHashTableを使用することです。これにより、O(n)の複雑さが改善されますが、n>>lの場合に2より高速であるとは限りません。良いニュースは、値を制限する必要がないことです。

私はあまりJavaではありませんが、これらのコーディングについてサポートが必要な場合は、お知らせください。

于 2012-05-21T01:38:02.497 に答える
1

これがあなたのプログラムのサンプル実装です。最も頻度の高いnoが返され、出現回数が最大の2つのnoが見つかった場合は、大きい方のnoが返されます。頻度を返したい場合は、コードの最後の行を「returnmf」に変更します。

{public int mode(int[]a,int n)
   {int i,j,f,mf=0,mv=a[0];
    for(i=0;i<n;i++)
       {f=0;
        for(j=0;j<n;j++)
           {if(a[i]==a[j])
               {f++;
               }
           }
        if(f>mf||f==mf && a[i]>mv)
           {mf=f;
            mv=a[i];
           }
       }
    return mv;        
   }

}

于 2012-05-21T04:03:39.980 に答える
1

この小さな子犬は機能します(数の代わりに頻度を返すように編集されています):

public static int mostFrequent(int[] numbers) {
    Map<Integer, AtomicInteger> map = new HashMap<Integer, AtomicInteger>() {
        public AtomicInteger get(Object key) {
            AtomicInteger value = super.get(key);
            if (value == null) {
                value = new AtomicInteger();
                super.put((Integer) key, value);
            }
            return value;
        }

    };

    for (int number : numbers)
        map.get(number).incrementAndGet();

    List<Entry<Integer, AtomicInteger>> entries = new ArrayList<Map.Entry<Integer, AtomicInteger>>(map.entrySet());
    Collections.sort(entries, new Comparator<Entry<Integer, AtomicInteger>>() {
        @Override
        public int compare(Entry<Integer, AtomicInteger> o1, Entry<Integer, AtomicInteger> o2) {
            return o2.getValue().get() - o1.getValue().get();
        }
    });

    return entries.get(0).getValue().get(); // return the largest *frequency*

    // Use this next line instead to return the most frequent *number*
    // return entries.get(0).getKey(); 
}

AtomicIntegerは、増分ごとに新しいオブジェクトが作成されないようにするために選択され、コードは少しすっきりと読み取れます。

匿名マップクラスは、「ifnull」コードを一元化するために使用されました

テストは次のとおりです。

public static void main(String[] args) {
    System.out.println(mostFrequent(new int[] { 2, 4, 3, 2, 2, 1, 4, 2, 2 }));
}

出力:

5
于 2012-05-21T04:12:05.433 に答える
1

整数のコレクションなので、どちらも使用できます

  1. 基数ソートはコレクションをソートし、O(nb)を取ります。ここで、bは整数を表すために使用されるビット数(Javaのプリミティブ整数データ型を使用する場合は32または64)、または
  2. 比較ベースのソート(クイックソート、マージソートなど)で、O(n log n)を取ります。

ノート:

  • nが大きくなるほど、基数ソートは比較ベースのソートよりも高速になる可能性が高くなります。nが小さい場合は、比較ベースのソートを使用した方がよいでしょう。
  • コレクション内の値の範囲がわかっている場合、bは32(または64)よりもさらに小さくなり、基数ソートがより望ましいものになります。
于 2012-05-21T04:39:54.240 に答える
0

HashMapの使用:

  import java.util.HashMap;
public class NumberCounter {

   static    HashMap<Integer,Integer> map;
   static int[] arr = {1, 2, 1, 23, 4, 5, 4, 1, 2, 3, 12, 23};
   static int max=0;

   public NumberCounter(){


         map=new HashMap<Integer, Integer>();

    }

    public static void main (String[] args)
    {
        Integer newValue=1;
        NumberCounter c=new NumberCounter();

        for(int i=0;i<arr.length;i++){
            if(map.get(arr[i])!=null) {
                newValue = map.get(arr[i]);
                newValue += 1;
                map.put(arr[i], newValue);
            }
            else
                map.put(arr[i],1);


        }

        max=map.get(arr[0]);
        for(int i=0;i<map.size();i++){
         if(max<map.get(arr[i]))
             max=map.get(arr[i]);
        }
        System.out.print(max);

    }

}
于 2018-07-29T12:29:38.603 に答える