4

要素を持つ配列が与えられたx場合、合計すると 0 になる 4 つの数値を見つける必要があります。また、そのような合計がいくつ存在するかを判断する必要があります。

したがって、3 次時間には 3 つのネストされた反復子が含まれるため、最後の数値を検索するだけで済みます (二分探索を使用)。

代わりにデカルト積 (X と Y に同じ配列) を使用することで、すべてのペアとその合計を 2 次配列に格納できます。したがって、各合計dについて を探すだけです-d

これは、二次時間 (に近い) のようになります。

public static int quad(Double[] S) {
  ArrayList<Double> pairs = new ArrayList<>(S.length * S.length);
  int count = 0;

  for (Double d : S) {
    for (Double di : S) {
      pairs.add(d + di);
    }
  }

  Collections.sort(pairs);

  for (Double d : pairs) {
    int index = Collections.binarySearch(pairs, -d);
    if (index > 0) count++; // -d was found so increment
  }

  return count;
}

x特定の配列入力の場合)353であるため、解は528になるはずですが、代わりにこの解を使用すると257しか見つかりません。立方時間では、528 個の 4 和をすべて見つけることができます。

public static int count(Double[] a) {
  Arrays.sort(a);
  int N = a.length;
  int count = 0;

  for(int i = 0; i < N; i++) {
    for (int j = 0; j < N; j++) {
      for (int k = 0; k < N; k++) {
        int l = Arrays.binarySearch(a, -(a[i] + a[j] + a[k]));
        if (l > 0) count++;
      }
    }
  }
  return count;
}

ひょっとして double の精度が失われるのでしょうか?

編集:BigDecimal代わりに使用するdoubleことが議論されましたが、パフォーマンスに影響を与えるのではないかと心配していました. 配列で 353 個の要素しか扱っていませんが、これは何か意味があるのでしょうか?

編集編集: BigDecimal を間違って使用した場合は申し訳ありません。私はこれまで図書館を扱ったことがありません。したがって、複数の提案の後、代わりに BigDecimal を使用してみました

public static int quad(Double[] S) {
  ArrayList<BigDecimal> pairs = new ArrayList<>(S.length * S.length);
  int count = 0;

  for (Double d : S) {
    for (Double di : S) {
      pairs.add(new BigDecimal(d + di));
    }
  }

  Collections.sort(pairs);

  for (BigDecimal d : pairs) {
    int index = Collections.binarySearch(pairs, d.negate());
    if (index >= 0) count++;
  }

  return count;
}

したがって、257 個ではなく 261 個の解を見つけることができました。これは問題があることを示している可能性がありdouble、実際には精度が失われています。ただし、261 は 528 から遠く離れていますが、原因を突き止めることができません。

最終編集: これは恐ろしく醜いコードだと思いますが、それでも機能しているようです。すでに while を試していましたが、BigDecimal を使用すると、528 の一致すべてを取得できるようになりました。

二次時間に十分近いかどうかはわかりませんが、時間が教えてくれます。
私はあなたにモンスターを紹介します:

public static int quad(Double[] S) {
  ArrayList<BigDecimal> pairs = new ArrayList<>(S.length * S.length);
  int count = 0;

   for (Double d : S) {
    for (Double di : S) {
      pairs.add(new BigDecimal(d + di));
    }
  }

  Collections.sort(pairs);

  for (BigDecimal d : pairs) {
    BigDecimal negation = d.negate();
    int index = Collections.binarySearch(pairs, negation);
    while (index >= 0 && negation.equals(pairs.get(index))) {
      index--;
    }

    index++;

    while (index >= 0 && negation.equals(pairs.get(index))) {
      count++;
      index++;
    }
  }

  return count;
}
4

2 に答える 2

1

BigDecimalソリューションには、配列内の浮動小数点数の合計が 0 になる正確な精度が必須であるため、ここではなくクラスを使用するdouble必要があります。10 進数の値の 1 つが .1 だったら、困ったことになります。その 2 進数の分数は、 で正確に表すことはできませんdouble。例として、次のコードを取り上げます。

double counter = 0.0;
while (counter != 1.0)
{
    System.out.println("Counter = " + counter);
    counter = counter + 0.1;
}

これは 10 回実行されると予想されますが、counter が正確に 1.0 になることはないため、無限ループになります。

出力例:

Counter = 0.0
Counter = 0.1
Counter = 0.2
Counter = 0.30000000000000004
Counter = 0.4
Counter = 0.5
Counter = 0.6
Counter = 0.7
Counter = 0.7999999999999999
Counter = 0.8999999999999999
Counter = 0.9999999999999999
Counter = 1.0999999999999999
Counter = 1.2
Counter = 1.3
Counter = 1.4000000000000001
Counter = 1.5000000000000002
Counter = 1.6000000000000003
于 2014-02-27T20:30:23.267 に答える