ハッシュソリューションを最速でなければならないこと、それの顔に思われる - と確かに、それはおそらくサイズが2GBを超えることは本当に巨大な配列のためです。
int
アレイのサイズが最大50,000,000エレメントの場合、ソートされたアレイで動作する最適化アルゴリズムを使用する方が高速です。
ここで(それだけでソートする前に、要素の元のインデックスを示すために使用され、余分な配列が必要であることに注意してください)あなたがソートされた配列で使用できるアルゴリズムです:
public static Tuple<int, int> FindTwoSumInSortedList(IList<int> list, int[] indices, int sum)
{
for (int i = 0, j = list.Count - 1; i < j;)
{
int s = list[i] + list[j];
if (s == sum)
return new Tuple<int, int>(indices[i], indices[j]);
else if (s < sum)
++i;
else
--j;
}
return null;
}
それは少しかかります元のリストをソートする余分な作業:
int n = 10000000;
int[] array = new int[n];
...
var indices = Enumerable.Range(0, n).ToArray();
Array.Sort(array, indices);
result = FindTwoSumInSortedList(array, indices, target);
これは、余分な作業の膨大な量のように見えるんが、私の驚きに、それは20,000,000要素のアレイ上のハッシュアルゴリズムよりも性能が優れています。
下記のテストプログラムを投稿しています。批判のためです。私はFindTwoSumInSortedList()
アルゴリズムのサンプルデータをできるだけ厄介なものにしようとしました。私は私のPC上でリリースビルドから取得
結果は以下のとおりです。
n = 10,000,000
3031
(5000000, 5000001)
1292
(5000000, 5000001)
n = 20,000,000
6482
(10000000, 10000001)
2592
(10000000, 10000001)
n = 50,000,000
17408
(25000000, 25000001)
5653
(25000000, 25000001)
ですから、ソートとアルゴリズムは二倍の速以上で見ることができます。それは本当に私を驚かせた!
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
class Program
{
public static void Main()
{
int n = 10000000;
int[] array = new int[n];
var rng = new Random(18789);
for (int i = 0; i < n; ++i)
array[i] = rng.Next(0, n);
array[n/2] = n;
array[n/2 + 1] = n+1;
var sw = Stopwatch.StartNew();
// This is too slow to test:
//var result = FindTwoSum(array, n*2+1);
//Console.WriteLine(sw.ElapsedMilliseconds);
//Console.WriteLine(result);
sw.Restart();
var result = FindTwoSumFaster(array, n*2 + 1);
Console.WriteLine(sw.ElapsedMilliseconds);
Console.WriteLine(result);
sw.Restart();
var indices = Enumerable.Range(0, n).ToArray();
Array.Sort(array, indices);
result = FindTwoSumInSortedList(array, indices, n*2+1);
Console.WriteLine(sw.ElapsedMilliseconds);
Console.WriteLine(result);
}
public static Tuple<int, int> FindTwoSum(IList<int> list, int sum)
{
for (int i = 0; i < list.Count; i++)
{
int sum2 = sum - list[i];
int index = list.IndexOf(sum2);
if (index > 0)
{
return new Tuple<int, int>(i, index);
}
}
return null;
}
public static Tuple<int, int> FindTwoSumInSortedList(IList<int> list, int[] indices, int sum)
{
for (int i = 0, j = list.Count - 1; i < j;)
{
int s = list[i] + list[j];
if (s == sum)
return new Tuple<int, int>(indices[i], indices[j]);
else if (s < sum)
++i;
else
--j;
}
return null;
}
public static Tuple<int, int> FindTwoSumFaster(IList<int> list, int sum)
{
if (list == null)
throw new NullReferenceException("Null list");
// constructing a hashset to have O(1) operations
var listSet = new HashSet<int>();
// number -> index mapping
// O(n) complexity
var listReverseSet = new Dictionary<int, int>();
int i = 0;
foreach (var elem in list)
{
if (!listSet.Contains(elem))
listSet.Add(elem);
listReverseSet[elem] = i++;
}
// O(n) complexity
int listCount = list.Count;
for (int index = 0; index < listCount; index++)
{
var elem = list[index];
if (listSet.Contains(sum - elem))
return new Tuple<int, int>(index, listReverseSet[sum - elem]);
}
return null;
}
}
}
すべてのペアまたは任意のペア? – Carlos
リストの代わりに配列を使用すると、リストが高速になります。また、 '100、100、5、100、100'と' 10 'の目標合計について正しい答えが得られますか?同じインデックスを2度与えます。 –
あなたの入力配列がソートされていることに気付きました。これはすべての場合に当てはまりますか? –