8

私が探しているのは、基本的な操作です (これには、atm を知らない名前があると確信しています)。次のようなマトリックスがあります。

{1,2,3}

{A、N、F}

{7,8,9}

私が突然変異したい

{1,A,7}

{2,N,8}

{3,F,9}

(上記は、実際の値ではなく、オブジェクトの識別子にすぎません。実際のオブジェクトは同じ型で、順序付けされていません)

私はそれに対する宣言的な解決策を好みますが、速度が要因です。かなりの数のテーブル (1 分あたり 100k セル) を回す必要があり、遅いバージョンはクリティカル パスになります。

しかし、私はまだ読みやすいソリューションに興味があります。以下の代替ソリューションを探しています。(代替とは、バリエーションを意味するのではなく、異なるアプローチを意味します)

var  arrays = rows.Select(row => row.ToArray());
var cellCount = arrays.First().Length;
for(var i = 0;i<cellCount;i++){
  yield return GetRow(i,arrays);
}

IEnumerable<T> GetRow(int i,IEnumerable<T[]> rows){
  foreach(var row in rows}{
     yield return row[i]; 
  }
}

2 つのほぼ同等に読みやすいソリューションの中で、私はより速い方を選びますが、読みやすさは速度より優先されます

編集 常に正方行列になります

4

6 に答える 6

11

私はこの実装について少し不安です。イテレータにローカルな副作用がありますが、私には論理的にきれいに見えます。これは、各シーケンスが同じ長さであることを前提としていますが、どのシーケンスでも機能するはずです。可変長Zip()メソッドと考えることができます。動作に必要な最小限の操作のみを使用するため、他の回答にある他のリンクされた LINQ ソリューションよりも優れたパフォーマンスを発揮するはずです。おそらく、LINQ を使用しないほうがよいでしょう。最適とさえ考えられるかもしれません。

public static IEnumerable<IEnumerable<T>> Transpose<T>(this IEnumerable<IEnumerable<T>> source)
{
    if (source == null) throw new ArgumentNullException("source");
    var enumerators = source.Select(x => x.GetEnumerator()).ToArray();
    try
    {
        while (enumerators.All(x => x.MoveNext()))
        {
            yield return enumerators.Select(x => x.Current).ToArray();
        }
    }
    finally
    {
        foreach (var enumerator in enumerators)
            enumerator.Dispose();
    }
}
于 2011-02-18T09:58:05.527 に答える
3

ここにあるこの拡張メソッドを見てください

/// <summary>
/// Swaps the rows and columns of a nested sequence.
/// </summary>
/// <typeparam name="T">The type of elements in the sequence.</typeparam>
/// <param name="source">The source sequence.</param>
/// <returns>A sequence whose rows and columns are swapped.</returns>
public static IEnumerable<IEnumerable<T>> Transpose<T>(
         this IEnumerable<IEnumerable<T>> source)
{
    return from row in source
           from col in row.Select(
               (x, i) => new KeyValuePair<int, T>(i, x))
           group col.Value by col.Key into c
           select c as IEnumerable<T>;
}

パフォーマンスについてはよくわかりませんが、コードはエレガントに見えます。

于 2011-02-18T09:41:34.773 に答える
1

あなたの質問は、元のマトリックスを変更したいことを暗示しているようです。

その場合、行列を として保存できる場合、IList<IList<T>> matrixこれは機能しますが、正方行列の場合のみです。

for(int i = 0; i < matrix.Count; ++i)
{
    for(int j = 0; j < i; ++j)
    {
        T temp = matrix[i][j];
        matrix[i][j] = matrix[j][i];
        matrix[j][i] = temp
    }
}
于 2011-02-18T09:47:03.493 に答える
0

誰かが興味を持っているなら、ここに私のものがあります。これは Jeff のものと同じような方法で実行されますが、わずかに高速であるように見えます (これらの ToArrays() が必要であると仮定すると)。目に見えるループや一時的なものはなく、はるかにコンパクトです。

public static IEnumerable<IEnumerable<T>> Transpose<T>(
    this IEnumerable<IEnumerable<T>> source)
{
    return source
        .Select(a => a.Select(b => Enumerable.Repeat(b, 1)))
        .Aggregate((a, b) => a.Zip(b, Enumerable.Concat));
}

空のリストも処理する必要がある場合は、次のようになります。

public static IEnumerable<IEnumerable<T>> Transpose<T>(
    this IEnumerable<IEnumerable<T>> source)
{
    return source
        .Select(a => a.Select(b => Enumerable.Repeat(b, 1)))
        .DefaultIfEmpty(Enumerable.Empty<IEnumerable<T>>())
        .Aggregate((a, b) => a.Zip(b, Enumerable.Concat));
}

質問者が、行列は常に正方形になると書いていることに気付きました。この実装 (および jeffs) は一度に行全体を評価しますが、行列が正方であることがわかっている場合は、zip 関数をより適切な方法で書き直すことができます。

public static IEnumerable<IEnumerable<T>> Transpose<T>(
    this IEnumerable<IEnumerable<T>> source)
{
    return source
        .Select(a => a.Select(b => Enumerable.Repeat(b, 1)))
        .DefaultIfEmpty(Enumerable.Empty<IEnumerable<T>>())
        .Aggregate(Zip);
}

public static IEnumerable<IEnumerable<T>> Zip<T>(
    IEnumerable<IEnumerable<T>> first, 
    IEnumerable<IEnumerable<T>> second)
{
    var firstEnum = first.GetEnumerator();
    var secondEnum = second.GetEnumerator();

    while (firstEnum.MoveNext())
        yield return ZipHelper(firstEnum.Current, secondEnum);
}

private static IEnumerable<T> ZipHelper<T>(
    IEnumerable<T> firstEnumValue, 
    IEnumerator<IEnumerable<T>> secondEnum)
{
    foreach (var item in firstEnumValue)
        yield return item;

    secondEnum.MoveNext();

    foreach (var item in secondEnum.Current)
        yield return item;
}

このように、各要素は返されるまで評価されません。

于 2012-07-07T21:03:21.163 に答える
0

さて、ここで探しているのは変換T[][] -> T[][]です。周りにはたくさんのIEnumerabe<IEnumerable<T>>.Transpose()解決策がありますが、それらはすべて、一時的なルックアップ/キーを使用して列挙型をループすることに要約され、大量のパフォーマンスに関しては多くのことが望まれます。あなたの例は実際にはより速く動作します(ただし、2番目の foreach も失う可能性があります)。

まず、「LINQ が必要ですか」と尋ねます。転置行列の目的が何であるかを説明していません。実際に速度が問題である場合は、LINQ/foreach から離れて、昔ながらの方法で実行することをお勧めします (for inside for)。

于 2011-02-18T10:03:25.043 に答える