Установить ранг массива во время выполнения - PullRequest
5 голосов
/ 15 января 2010

Мне было интересно, как проще всего реализовать массив, ранг которого указан во время выполнения.

В примере, над которым я работаю, хранится массив логических значений для точек решетки, и я хочу, чтобы пользователь мог выбирать, сколько пространственных измерений использует модель во время выполнения.

Я посмотрел на метод Array.newInstance ():

dimensionOfSpace = userInputValue;  // this value comes from GUI or whatever
int latticeLength = 5;  // square lattice for simplicity

int[] dimensions = new int[dimensionOfSpace];
for(int i = 0; i < l.length; i++) l[i] = length; 
Object lattice = Array.newInstance(boolean.class, dimensions);

Но для доступа к этим значениям любым способом, по-видимому, требуются ужасно медленные методы, такие как рекурсивное использование Array.get до тех пор, пока возвращаемое значение больше не будет массивом, то есть с использованием isArray ().

Я упустил очевидное решение здесь? Я хотел бы иметь возможность получить доступ к значениям аналогично foo [i] [j] [k].

Ответы [ 4 ]

5 голосов
/ 15 января 2010

Похоже, что вы ищете какой-то способ объявить, сколько измерений массив имеет во время выполнения. Я не знаю, как это можно сделать с помощью многомерного ArrayList или любой многомерной структуры, где вы должны указать размерность во время компиляции.

Единственный ответ, который я вижу, - использовать простой линейный массив, заключенный в класс, который преобразует многомерную координату в ее положение в базовом массиве и обратно. Это в основном то, как языки, такие как C, хранят многомерные массивы, используя один непрерывный кусок памяти.

Код будет выглядеть примерно так:

import java.util.*;

class MultiArray<T>{
    private int[] dimensions;
    private Object[] array;

    public MultiArray(int ... dimensions){
        this.dimensions=dimensions;
        //Utils.product returns the product of the ints in an array
        array=new Object[Utils.product(dimensions)];
    }

    public void set(T value, int ... coords){
        int pos=computePos(coords); 
        array[pos]=value;
    }

    public T get(int ... coords){
        int pos=computePos(coords);
        return (T)(array[pos]);
    }

    private int computePos(int[] coords){
        int pos=0;
        int factor=1;
        for (int i=0;i<coords.length;i++){
            pos+=factor*coords[i];
            factor*=dimensions[i];
        }
        return pos;
    }
}

class Main{
    public static void main(String args[]){
        MultiArray<Integer> m=new MultiArray<Integer>(new int[]{5,4,3}); 
        Random r=new Random();

        for(int i=0;i<5;i++)
            for(int j=0;j<4;j++)
                for(int k=0;k<3;k++)
                    m.set(r.nextInt(),i,j,k);
        for(int i=0;i<5;i++){
            for(int j=0;j<4;j++){
                for(int k=0;k<3;k++)
                    System.out.print(m.get(i,j,k)+" ");     
                System.out.println("");
            }
            System.out.println("\n");
        }
    }
}

class Utils{
    public static int product(int...a){
        int ret=1;
        for (int x:a) ret*=x;
        return ret;
    } 
}
3 голосов
/ 15 января 2010

Я собираюсь использовать термин «ранг» для обозначения «числа измерений» в вашем массиве. Таким образом, вектор имеет ранг 1, матрица имеет ранг 2 и так далее. Вы уже приняли ответ, который по вашему собственному признанию не совсем то, что вы хотите. Вот альтернатива для расчета за меньшее:

Вспомните, что компьютерная память по существу линейна и что компилятор, когда он предоставляет вам массивы, на самом деле заботится о преобразовании выражения индекса в линейный адрес. Об этом проще всего думать, если предположить, что все массивы находятся в смежной памяти, а не всегда так. Предположим, что вы делаете объявление, такое как ARRAY_OF_TYPE [10] [10] [10], т.е. оно имеет 1000 элементов. Тогда элемент в позиции [3] [5] [4] имеет вид (мои массивы индексируются с 1, а не с 0 - измените суммы, которые следуют, если хотите) в местоположении baseAddress + 354 * size_of_element_of_TYPE.

Полагаю, вы знаете, куда я сейчас иду ...

Во время выполнения ваша программа запрашивает у пользователя список целых чисел. Каждое целое число определяет размер одного из измерений массива, число целых чисел указывает ранг массива. Ваша программа делает несколько умножений, и вы выделяете вектор правильной длины. Хорошо, вы должны написать функции индексации и деиндексации, но они должны быть достаточно простыми.

и вуаля у вас есть массив, ранг которого устанавливается во время выполнения.

3 голосов
/ 15 января 2010

Оформить заказ Коллекции Java . Он содержит класс ArrayList, размер которого увеличивается по мере необходимости.

Одномерный

List<Boolean> a = new ArrayList<Boolean>();

Двумерный

List<List<Boolean>> b = new List<List<Boolean>>();

Трехмерный

List<List<List<Boolean>>> c = new List<List<List<Boolean>>>();

И вы получите доступ к элементу как c.get(i).get(j).get(k) вместо c[i][j][k], как в трехмерном массиве. Или, что еще лучше, оберните его в свой собственный класс и используйте там метод get(). Так и становится:

c.get(i, j, k);

Редактировать

Чтобы получить многомерный список глубины N, удалите указатель типа Boolean и просто создайте списки как

List level1 = new ArrayList();
List level2 = new ArrayList();
List level3 = new ArrayList();
level1.add(level2);
level2.add(level3);

и т. Д. *

1 голос
/ 15 января 2010

Я быстро выполнил поиск в Google по запросу "java тензор", который нашел DJEP , это может быть что-то, что соответствует вашему счету?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...