5

複数の int 配列があります。

1) [1 , 202,4  ,55]
2) [40, 7]
3) [2 , 48 ,5]
4) [40, 8  ,90]

すべての位置で最大の数を持つ配列を取得する必要があります。私の場合、それは配列 #4 になります。説明:

  • 配列 #2、#4 は 1 番目の位置に最大の数があるため、最初の反復の後、これら 2 つの配列が返されます ([40, 7] および [40, 8 ,90])。
  • 前の反復から返された配列の 2 番目の位置を比較した後、8 > 7 であるため、配列 #4 を取得します。
  • 等々...

このための効率的なアルゴリズムを提案できますか? Linq を使用することをお勧めします。

アップデート

長さに制限はありませんが、任意の位置の数値が大きくなるとすぐに、この配列が最大になります。

4

7 に答える 7

1
var allArrays = new[]
{
    new[] { 1, 202, 4, 55 },
    new[] { 40, 7 },
    new[] { 2, 48, 5 },
    new[] { 40, 8, 90 }
};

比較するインデックスを見つけます。

比較するものが何もないため、3 番目のインデックスを除外します。つまり、1 番目の配列には 55 しかありません。

データ

var maxElementsCount = allArrays.GroupBy(p => p.Length)
    // Find 2 at least
    .Where(p => p.Count() > 1)
    // Get the maximum
    .OrderByDescending(p => p.Count())
    .First().Key;

// 0, 1, 2
var indexes = Enumerable.Range(0, maxElementsCount);

スライスを取得します。

スライス表

var slices = indexes.Select(i =>
    allArrays.Select((array, arrayNo) => new
    {
        ArrayNo = arrayNo,
        // null if the element doesn't exist
        Value = i < array.Length ? array[i] : (int?)null
    }))
    .ToArray();

スライス

// Get the max values in each slice
var maxValues = slices.SelectMany(slice =>
    {
        var max = slice.Max(i => i.Value);
        return slice.Where(i => i.Value == max);
    })
    .ToArray();

最大値

// Get the result array no
var arrayNumber = maxValues.GroupBy(p => p.ArrayNo)
    .OrderByDescending(p => p.Count())
    .First().Key;

var res = allArrays[arrayNumber];

結果

于 2012-10-28T15:15:04.317 に答える
1

a) *効率的な*ものではありませんが、ワンライナーを書くのは簡単です。

var arr1 = new int[] { 1, 202, 4, 55 };
var arr2 = new int[] { 40, 7 };
var arr3 = new int[] { 2, 48, 5 };
var arr4 = new int[] { 40, 8, 90 };

var max = new int[][] { arr1, arr2, arr3, arr4 }
           .Select(arr => new { 
                   IArray = arr, 
                   SArray = String.Join("",arr.Select(i => i.ToString("X8"))) 
           })
           .OrderByDescending(x => x.SArray)
           .First()
           .IArray;

b) `IComparer` の実装によるより良いもの

public class ArrayComparer : IComparer<int[]>
{
    public int Compare(int[] x, int[] y)
    {
        for(int i=0;i < Math.Min(x.Length,y.Length);i++)
        {
            if (x[i] > y[i]) return 1;
            if (x[i] < y[i]) return -1;
        }
        return x.Length - y.Length;
    }
}

var max2 = new int[][] { arr1, arr2, arr3, arr4 }
           .OrderByDescending(x => x, new ArrayComparer())
           .First();

c) 最高のもの

var arrays = new int[][] { arr1, arr2, arr3, arr4 };
var max3 = arrays[0];
ArrayComparer comparer = new ArrayComparer();
for (int i = 1; i < arrays.Length; i++)
{
    if(comparer.Compare(arrays[i],max3)>0) max3 = arrays[i];
}

d) "Max" を拡張したジェネリック バージョン

var max4 = new int[][] { arr1, arr2, arr3, arr4 }
            .Max(new SOExtensions.Comparer<int>())
            .ToArray();

public static class SOExtensions
{
    public static IEnumerable<T> Max<T>(this IEnumerable<IEnumerable<T>> lists, IComparer<IEnumerable<T>> comparer)
    {
        var max = lists.First();
        foreach (var list in lists.Skip(1))
        {
            if (comparer.Compare(list, max) > 0) max = list;
        }
        return max;
    }

    public class Comparer<T> : IComparer<IEnumerable<T>> where T: IComparable<T>
    {
        public int Compare(IEnumerable<T> x, IEnumerable<T> y)
        {
            foreach(var ab in  x.Zip(y,(a,b)=>new{a,b}))
            {
                var res=ab.a.CompareTo(ab.b);
                if (res != 0) return res;
            }
            return x.Count() - y.Count();
        }
    }
}

結論

私のテストケースでの相対的なパフォーマンス:4000T, 270T, T, 6T

したがって、速度を求める場合は、Sort/OrderBy を使用するアルゴリズムを使用しないでください。そのコストはO(N*Log(N))であるため(Max はO(N)です) 。

于 2012-10-28T22:33:37.130 に答える
1

LINQ はあまり効率的ではありません (たとえば、LINQ と FOREACH と FORを参照してください)。ただし、非常に読みやすくなっています。LINQ が提供するよりも優れたパフォーマンスが実際に必要な場合は、LINQ を使用せずにコードを記述する必要があります。ただし、最適化が必要であることがわかる前に最適化を行うべきではありません。

これはパフォーマンスのために特別に調整されたものではありませんが、問題に対する明確で読みやすい解決策です。

static int[] FindLargestArray(int[][] arrays)
{
    for (int i = 0; arrays.Length > 1 && i < arrays.Max(x => x.Length); i++)
    {
        var maxVal = arrays.Where(x => i < x.Length).Max(x => x[i]);
        arrays = arrays.Where(x => i < x.Length && x[i] == maxVal).ToArray();
    }
    return arrays[0]; //if more than one array, they're the same, so just return the first one regardless
}

状況によっては、これで十分なパフォーマンスが得られる場合もあります。

于 2012-10-28T14:12:48.753 に答える
0

私は従来のオブジェクト指向アプローチを採用し、ラッパーを作成します。

public class SpecialArray<T> : IComparable<SpecialArray<T>>
    where T : IComparable
{
    public T[] InternalArray { get; private set; }

    public SpecialArray(T[] array)
    {
        InternalArray = array;
    }

    public int CompareTo(SpecialArray<T> other)
    {
        int minLength = Math.Min(InternalArray.Length, other.InternalArray.Length);

        for ( int i = 0; i < minLength; i++ )
        {
            int result = InternalArray[i].CompareTo(other.InternalArray[i]);

            if ( result != 0 )
                return result;
        }

        return 0;
    }
}

次に、次のようにmaxを検索できます。

        var list = new[]
            {
                new SpecialArray<int>(new[] {1, 202, 4, 55}),
                new SpecialArray<int>(new[] {40, 7}),
                new SpecialArray<int>(new[] {2, 48, 5}),
                new SpecialArray<int>(new[] {40, 8, 90})
            };

        var max = list.Max();
于 2012-10-28T14:46:20.427 に答える
0

まったくないものLinq

public static List<int[]> FindTheHighestArrays(List<int[]> lst)
{
    List<KeyValuePair<int[], int>> temp = new List<KeyValuePair<int[], int>>();
    List<int[]> retList = lst;
    lst.Sort((x, y) => x.Length.CompareTo(y.Length));
    int highestLenght = lst[lst.Count - 1].Length;
    for (int i = 0; i < highestLenght; i++)
    {
        temp.Clear();

        foreach (var item in retList)
        {
            if (item.Length <= i)
                continue;

            temp.Add(new KeyValuePair<int[], int>(item, item[i]));
        }

        temp.Sort((x, y) => x.Value.CompareTo(y.Value));
        retList.Clear();
        retList.AddRange(temp.FindAll(kvp => kvp.Value == temp[temp.Count - 1].Value).ConvertAll(f => f.Key));

        if (retList.Count == 1)
            return retList;
    }

    return retList;
}

上記の関数は、最も高い int 配列のリストを返します。たとえば、試してみると、

1) [1, 202, 4, 55]

2) [1, 202, 4, 55]

関数は両方の配列を返します。これは、どちらも等しく最大であるためです。このような場合に 1 つの配列のみが必要な場合は、戻り値の型を変更してリストの最初の要素を返すだけです。

次のように呼び出すことができます。

int[] a = new int[] { -1, 31, 90 };
int[] b = new int[] { -1, 31, 89 };
int[] c = new int[] { 0, 0, 90 };
List<int[]> lst = new List<int[]>() { a, b, c };

var highestArrays = FindTheHighestArrays(lst);
于 2012-10-28T16:34:05.140 に答える
0

「二分探索木」をお勧めします。最小要素と最大要素にすばやくアクセスできます。

http://en.wikipedia.org/wiki/Binary_search_tree

配列ごとに 1 つのツリーと、配列を結合する 1 つのツリーが必要になります。

ツリーを大きくする予定がある場合は、ツリーを簡単に最適化するための「Red-Black BST」アルゴリズムを検索することもできます。

編集:

通常の BST はそれ自体は高速ですが、要素を追加するとツリーのバランスがかなり崩れます。たとえば、新しく追加されたすべての変数の値が平均して前の値よりも大きい場合、最大値へのパスはますます長くなります。 、一方、最小値への道は非常に短いままです。赤と黒の BST はバランスが取れており、最も離れた要素 (最小と最大を含む) へのパスは等しく短くなります。より高速なタイプの BST がありますが、私の知る限り、それらはコーディングが困難です。

ただし、赤黒 BST を使用する場合は、まず通常の BST をマスターすることをお勧めします。

于 2012-10-28T14:00:05.933 に答える
0

再帰で解決できる問題のように見えます。

public void GetMax()
{
    var matrix = new[]
                {
                    new[] {1, 202, 4, 55},
                    new[] {40, 7},
                    new[] {2, 48, 5},
                    new[] {40, 8, 90}
                };
    var result = GetMaxRecursive(matrix).FirstOrDefault();
}

private static int[][] GetMaxRecursive(int[][] matrix, int level = 0)
{
    // get max value at this level
    var maxValue = matrix.Max(array => array.Length > level ? int.MinValue : array[level]);

    // get all int array having max value at this level
    int[][] arraysWithMaxValue = matrix
        .Where(array => array.Length > level && array[level] == maxValue)
        .ToArray();

    return arraysWithMaxValue.Length > 1
                ? GetMaxRecursive(arraysWithMaxValue, ++level)
                : arraysWithMaxValue;
}
于 2012-10-28T14:31:09.807 に答える