Как установить значения в многомерном массиве с помощью линейного индекса

Каков наиболее эффективный способ задания значений в многомерных массивах C # с использованием линейного индекса? Например, данный массив ...

int[,,] arr2 = {   {{0,1,2}, {3,4,5}, {6,7,8}}
                , {{9,10,11}, {12,13,14}, {15,16,17}}
                , {{18,19,20}, {21,22,23}, {24,25,26}}
        };

Как мне установить все элементы на 30, используя линейный индекс ...

//This code does not work
for (int i = 0; i < arr.Length; i++)
{
    arr.SetValue(30, i);
}

Очевидно, SetValue () выше не работает с многомерными массивами.

Вот лучшее решение, которое я мог придумать ...

РЕДАКТИРОВАТЬ: Добавлены некоторые пояснения к коду ...

static class Program
{
    static void Main(string[] args)
    {
        //Sample input. 
        int[,,] arr2 = {   {{0,1,2}, {3,4,5}, {6,7,8}}
                        , {{9,10,11}, {12,13,14}, {15,16,17}}
                        , {{18,19,20}, {21,22,23}, {24,25,26}}
                };

        int[] arr1 = { 1, 2, 3, 4 };

        setElementsTo30(arr2);
        setElementsTo30(arr1);

    }

    //Must be able to process int arrays of arbitrary dimensions and content
    private static void setElementsTo30(Array arr)
    {
        IList<int> cumulativeLength = getCumulativeLengths(arr);

        for (int i = 0; i < arr.Length; i++)
        {
            SetValue(arr, i, 30, cumulativeLength);
        }
    }

    public static void SetValue(this Array arr, int index, object value, IList<int> cumulativeLength)
    {
        int[] arrayIndex = new int[arr.Rank];

        for (int dim = arr.Rank-1; dim >= 0; dim--)
        {
            arrayIndex[dim] = index / cumulativeLength[dim] % arr.GetLength(dim);
        }

        arr.SetValue(value, arrayIndex);
    }

    private static IList<int> getCumulativeLengths(Array arr)
    {
        List<int> lengths = new List<int>(arr.Rank);

        for (int dim = 0; dim < arr.Rank; dim++)
        {
            int prod = 1;
            for (int i = dim + 1; i < arr.Rank; i++)
            {
                prod *= arr.GetLength(i);
            }
            lengths.Add(prod);
        }

        return (IList<int>)lengths;
    }
}

Есть ли способ сделать то же самое более эффективно и, возможно, использовать что-то, предоставляемое самой платформой (то есть то, что можно использовать без особых хлопот).

Спасибо,
SDX2000.

11.12.2008 16:10:28
4 ОТВЕТА
РЕШЕНИЕ

зачем тебе IList?

static void SetValue2(this Array a, object value, int i) {
    int[] indices = new int[a.Rank];
    for (int d = a.Rank - 1; d >= 0; d--) {
        var l = a.GetLength(d);
        indices[d] = i % l;
        i /= l
    }
    a.SetValue(value, indices);
}

Тестовый код:

static void Main(string[] args) {
    int[, ,] arr2 = {   
        {{0,1,2}, {3,4,5}, {6,7,8}}, 
        {{9,10,11}, {12,13,14}, {15,16,17}}, 
        {{18,19,20}, {21,22,23}, {24,25,26}}
    };
    for (int i = 0; i < arr2.Length; i++) {
        arr2.SetValue2(30, i);
    }
}
2
12.12.2008 16:06:28
Хорошо, я наконец дошел до этого и должен сказать, что вы проделали хорошую работу. Спасибо за помощь. В моей первой версии был внутренний цикл for, который вычислял совокупный размер для более низких измерений. Я сохранил эти итоги в список, чтобы повысить (время) эффективность.
Autodidact 12.12.2008 08:44:17

SetValue()должно сработать. Посмотрите на это для немного больше вдохновения.

РЕДАКТИРОВАТЬ: Не могли бы вы просто сделать

{{30,30,30}, {30,30,30}, {30,30,30}}
 , {{30,30,30}, {30,30,30}, {30,30,30}}
  , {{30,30,30}, {30,30,30}, {30,30,30}

}

Как примечание, вы уверены, что хотите вернуть IList<int>от getCumulativeLengths?

Я всегда думал, быть щедрым на входе и строгим на выходе.

0
11.12.2008 16:35:01
1. Да, я ограничен прямоугольными массивами 2. Нет возврата IList <int> было поспешным решением. В процессе профессионального программирования я бы вернул коллекцию только для чтения. 3. Мой принцип - будь строг на входе и выходе. Я мог бы предоставить некоторые ссылки, если бы я мог вспомнить :(
Autodidact 11.12.2008 16:23:25
Хм ... Теперь я понимаю, что я немного поспешно отвечал. Я не совсем уверен, что вы можете установить все линейно. Возможно, вам придется перейти во вложенный цикл. Я продолжу читать.
Nicholas Mancuso 11.12.2008 16:24:45
@ EDIT - На самом деле это просто демонстрационная программа, у меня нет априорной информации о входном массиве (мой код будет использоваться в качестве библиотеки).
Autodidact 11.12.2008 16:32:23

Вы знаете, сколько кортежей будет существовать изначально? Если вы скажете матрицу с размерами axbxcxd, не могли бы вы использовать следующее, чтобы получить список всех индексов:

for i=0 to (a*b*c*d)

       Array[i % a, (i/a) % b, (i/(a*b) % c, i / (a*b*c)] = 30

Таким образом, когда счетчик переворачивает различные границы, каждый последующий индекс увеличивается. Если их больше, это обобщает n-кортеж, который просто умножает предыдущие значения. Можно было бы изменить арифметику индексов, если бы захотелось пройти другим путем.

1
11.12.2008 16:53:29
@JB массивы могут иметь любое количество измерений.
Autodidact 11.12.2008 16:56:55
    public static void CopyToMultidimensionalArray(this IList<object> source, Array target, IList<int> dimensions)
    {
        var indices = new int[dimensions.Count];
        for (var i = 0; i < source.Count; i++)
        {
            var t = i;
            for (var j = indices.Length - 1; j >= 0; j--)
            {
                indices[j] = t % dimensions[j];
                t /= dimensions[j];
            }

            target.SetValue(source[i], indices);
        }
    }
0
3.07.2017 19:30:53