LINUX.ORG.RU

[Какбыэдак] Подскажите решение алгоритма поэлегантнее


0

2

Есть у меня n-мерный массив m. Величина n меняется в зависимости от мимолетного желания.

Всю эту беду надо перелопатить в цикле таким образом:

for i in r(100):
    m(1)
    for j in r(100):
        m(2)
        for ? in r(100):
            m(n)

В принципе все рекурсией можно сделать, но есть ли другое решение?

★★★★★

для циклов, говорят, очень подходит обработка в стиле OpenMP, если зависимостей между итерациями нет. Скорость обработки может увеличится в несколько раз, в зависимости от числа рабочих ядер.

mrs
()
Ответ на: комментарий от cnupm

python - но вообще какая разница, если вопрос об алгоритме?

Siado ★★★★★
() автор топика
Ответ на: комментарий от Siado

тут соль не в скорости


а тогда зачем? писать функцианально? фильтрами, фолдами и мапами?

Karapuz ★★★★★
()

Только хардкор! Только кодогенерация! Только ЛNSП.

k_andy ★★★
()
Ответ на: комментарий от ugoday

Да, и вообще, что означает оператор m(1)?

Miguel ★★★★★
()
file = new File("svoboda-ravenstvo-upyachka-python.py");

try {
for (i=0; i<iMax; i++) {
   file.write(tabs+"for i"+i+" in r(100): m("+i+")");
   tabs = tabs+tabs;
}
} finally {
   file.save();
}

System.exec("python2.exe "+file.getPath());
stevejobs ★★★★☆
()

Многомерные массивы эмулируются при помощи одномерного на основе пересчета смещения. Если размерность массива n+1 и размеры массива sz_0 x sz_1 x ... sz_n то для элемента с координатами (i_0,i_1,...,i_n) смещение от начала одномерного массива будет (как один из вариантов) i_0 + i_1*sz_0 + i_2*sz_0*sz1 + ... i_n*sz_0*sz_1*...*sz_{n-1}. Весь массив проходится одним циклом (вдоль одномерного массива), если нужно узнать координаты элемента по смещению это легко делается при помощи операции деления по модулю.

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

Если массивы большие и важна скорость доступа к ближ соседям элемента, лучше юзать локально-рекусривные структуры данных (но тут уже не питон а C/C++)

AIv ★★★★★
()
Ответ на: комментарий от Siado

> массив m[0] - m[n]

А где здесь n-мерность? Вижу только одномерный массив от 0 до n включительно.

anonymous
()

man Рекурсия

Led ★★★☆☆
()
Ответ на: комментарий от Siado

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

AIv ★★★★★
()
Ответ на: комментарий от Siado

Вы задачу четко сформулировать можете? Нет? Тогда ждите телепата. Ссылка приведенная Вами не является четкой формулировкой задчаи, она является решением непонятно какой задачи на не очень понятно каком ЯП.

AIv ★★★★★
()
Ответ на: комментарий от ugoday

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

Есть массив m длиной n. Нужно перебрать в циклах все переменные массива, чтобы получилась подобная конструкция:

Теперь пример на С++ :

for (int i; i<100; i++)
{
    for (int j; i<100; i++)
    {
        for ... {
            for (int n; i<100; i++)
            {
                cout << m[i]
                cout << m[j]
                ...
                cout << m[n]
            }
        }
    }      
}

И без рекурсии )

Siado ★★★★★
() автор топика
Ответ на: комментарий от Siado

Я Вам выше уже фактически предлагал вариант. Всего надо произвести 100х100х...х100 операций - делаете по ним один цикл от 0 до 100**n, пусть меняется переменная i. Дальше в теле цикла нужно понять, когда и в тело какого вложенного цикла Вы входите - операторы % и / Вам в помощь. Грубо говоря, если i%100**k == 0 - Вы вошли в тело цикла уровня k.

AIv ★★★★★
()
Ответ на: комментарий от AIv

точнее в тело цикла n-k если считать снаружи вовнутрь ;-)

AIv ★★★★★
()
Ответ на: комментарий от dave

Я не знаю чем ТС не нравится рекурсия, ну да что ж тут поделаешь...

AIv ★★★★★
()

> массив

Откуда в питоне массивы? *кулфейс*

А вообще, совершенно не понял что именно требуется.

pevzi ★★★★★
()
Ответ на: комментарий от Siado

Вообще-то, по твоей новой версии, получается, что массив m должен быть длиной 100, а не n. Потому как если, скажем, n=3 и массив m имеет длину 3, то, запустив твой код, мы получим кучу мусора.

Это если не брать в расчёт очевидные ошибки в записи цикла.

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

Рабочая гипотеза: тебе нужно следующее

int m[100];
int n = getNumLoops(); //какая-то там функция
for (int i1 = 0; i1 < 100; i1++) {
  for (int i2 = 0; i2 < 100; i2++) {
    ...
      for (int in = 0; in < 100; in++) {
        std::cout << m[i1] << std::endl;
        std::cout << m[i2] << std::endl;
        ...
        std::cout << m[in] << std::endl;
      }
    ...
  }
}
Вникай, как это делается на Хаскеле.

Начнём с того, что абстрагируем содержимое внутреннего цикла; скажем просто, что у нас будет некая функция action, которая по списку [i1,i2,...,in] производит некоторые действия.

Если бы (если бы!) требовался только один цикл (и функция action принимала бы аргументом не список, а одно число), то всё было бы просто:

loop action = forM_ [0..99] action
Из этого loop можно сделать нужную нам функцию
loops n action = runContT $ replicateM n $ ContT loop
Тут используется некоторое количество магии с континуэйшенами, но всё работает. Для просветления - http://migmit.livejournal.com/37145.html .

Остаётся собрать эту самую action. Если она такая, как показано в примере, то можно написать, например, так:

printValues inds = forM_ inds $ \index -> print $ m !! index

Вызывать так:

loops n printValues

Miguel ★★★★★
()
Ответ на: комментарий от Siado

Чем больше вы объясняете роль массива m, тем менее понятной она становится.

Вот функциональное решение для перебора всех комбинаций из n чисел от 0 до 100:

def genfunc ():
  def func (indices = []):
    for i in xrange(100):
      if func.prev:
        func.prev([i] + indices)
      else:
        print [i] + indices
  return func

n = 3

f = None
for j in xrange(n):
  newfunc = genfunc()
  newfunc.prev = f
  f = newfunc

f()
ringill
()
Ответ на: комментарий от anonymous

Прекрасный совет! :)
Поздравляю! :)

Это из серии вопрос-ответ:
- Девушка, на витрине йогурт свежий?
- Персиковый!

Dikar ★★
()
Ответ на: комментарий от Dikar

> Прекрасный совет! :)

Что не так? Я не ставил целью ответить на вопрос топикстартера.

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

anonymous
()
Ответ на: комментарий от Siado

А ничего что в твоём этом примере единственное, что будет выполнено, это много-много раз

for (int i; i < 100; i++) { std::cout << m[i]; }
?

NightmareZ
()

Вон из профессии, ящитаю.

Ты не способен по-русски объяснить, чего именно хочешь добиться этими извращениями?

NightmareZ
()
Ответ на: Вон из профессии, ящитаю. от NightmareZ

Может нужно просто перебрать все возможные комбинации элементов из массива m? Тогда делается это элементарно:

int m[] = { 1, 2, 3 };
int n = sizeof(m) / sizeof(int);

std::vector<int> vec(n);

do
{
  for (int i = 0; i < n; i++) {
    std::cout << m[vec[i]] << " ";
  }

  std::cout << std::endl;
  vec[n - 1]++;

  for (int i = n - 1; i > 0; i--)
    if (vec[i] >= n) {
      vec[i] = 0;
      vec[i - 1]++;
  }
} while (vec[0] < n);

NightmareZ
()

ТС болен шизофазией.Какие-то алгоритмы (неясно что делающие), матрицы, которые являются то ли сечением, то ли элементом.

aedeph
()

Уважаемый, может вы лучше задачу напишете полностью? Мой, пока здоровый от пайтона, мозг не может понять зачем нужен рандомно-мерный массив, который к тому же надо обработать весь и сразу.

Dikar ★★
()
Ответ на: комментарий от NightmareZ

>Может нужно просто перебрать все возможные комбинации элементов из массива m?

Да, все верно вот так оно объясняется, именно это и надо, но что-то сразу не осилил в условии написать. :D

З.ы.
Поздравляю с развитием телепатии.

Siado ★★★★★
() автор топика
Ответ на: комментарий от Siado

Лол, чуваку всего-то надо явно построить декартово произведение массива самого на себя n-ой степени. Вот к чему приводит отсутствие фундаментального образования.

Matlab, APL, J во все поля. Не надо изобретать велосипедов.

anonymous
()
Ответ на: комментарий от anonymous

Решение на J:

{ n $ <v

v - исходный массив, n - степень декартова самопроизведения.

anonymous
()
Ответ на: комментарий от anonymous

Точно, и я тоже ступил...

на питоне в общем виде можно напр так

reduce( lambda R, L : [ r+(v,) for r in R for v in L ], LL, [()] )

где LL - последовательность последовательностей которые надо декартово перемножать, в случ ТС

LL = [range(100)]*n

Если узкозаточенное для ТС решение

reduce( lambda R, i : [ r+(v,) for r in R for v in range(100) ], range(n), [()] )

AIv ★★★★★
()
Ответ на: комментарий от geekless

Либо рекурсия, либо в явном виде юзать стек, либо кодогенерация.

У меня вон в коде нет ни рекурсии, ни стека, ни кодогенерации. Я что-то сделал не так?

З.Ы. Насколько я знаю, мой вариант не самый простой. На плюсах кошернее использовать std::next_permutation. Но ввиду того, что на плюсах я не пишу, извиняйте.

NightmareZ
()
Ответ на: комментарий от NightmareZ

Ну таки да: заводим генератор, который будет выдавать последовательность индексов, и делаем, собственно, обработчик, который будет для каждого составного индекса разыменовывать и обрабатывать конечный элемент. На питоне покатит.

Но раз у тебя код на плюсах, мне интересно, как ты будешь вызывать operator [] невычислимого на этапе компиляции типа данных. Заведешь отдельный класс, представляющий n-мерный срез массива?

geekless ★★
()
Ответ на: комментарий от NightmareZ

Почитав сейчас маны, я так понял, next_permutation несколько по-другому работает (в отличии от моего кода). У меня элементы могут повторяться, а в next_permutation - нет.

Код

int m[] = { 1, 2, 3 };
int n = sizeof(m) / sizeof(int);

do {
  for (int i = 0; i < n; i++)
    std::cout << m[i] << " ";
  std::cout << std::endl;
} while ( std::next_permutation (m, m + n) );
выведет
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1

NightmareZ
()
Ответ на: комментарий от NightmareZ

То есть, чтобы добиться такого же результата без алгоритма std::next_permutation, мой старый код нужно переписать как-то так:

(ещё раз прошу простить меня адептов C++ за возможный говнокод, ибо на нём я не пишу)

int m[] = { 1, 2, 3 };
int n = sizeof(m) / sizeof(int);

std::vector<int> vec(n);

do {
  std::vector<int> check(n);
  bool correct = true;
		
  for (std::vector<int>::iterator it1 = vec.begin(); it1 != vec.end(); it1++) {
    for (std::vector<int>::iterator it2 = vec.begin(); it2 != vec.end(); it2++) {
      if (it1 != it2 && *it1 == *it2) {
        correct = false;
        break;
      }
    }

    if (!correct)
      break;
  }

  if (correct) {
    for (int i = 0; i < n; i++) {
      std::cout << m[vec[i]] << " ";
    }

    std::cout << std::endl;
  }

  vec[n - 1]++;

  for (int i = n - 1; i > 0; i--)
    if (vec[i] >= n) {
      vec[i] = 0;
      vec[i - 1]++;
    }
} while (vec[0] < n);
NightmareZ
()
Ответ на: комментарий от NightmareZ
using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static IEnumerable<int[]> Generator(int[] arr)
    {
        var counter = new int[arr.Length];

        do
        {
            if (counter.SequenceEqual(counter.Intersect(counter)))
            {
                var result = new List<int>(arr.Length);

                for (int i = 0; i < arr.Length; i++)
                    result.Add(arr[counter[i]]);

                yield return result.ToArray();
            }

            counter[counter.Length - 1]++;

            for (int i = counter.Length - 1; i > 0; i--)
                if (counter[i] >= arr.Length)
                {
                    counter[i] = 0;
                    counter[i - 1]++;
                }
        } while (counter[0] < arr.Length);
    }

    static void Main()
    {
        var m = new[] { 1, 2, 3 };

        foreach (int[] arr in Generator(m))
        {
            foreach (int x in arr)
                Console.Write("{0} ", x);
            Console.WriteLine();
        }
    }
}
NightmareZ
()
Ответ на: комментарий от NightmareZ

и при чём тут n-мерные массивы.

Действительно, ни при чём, т.к. я не заметил вот этого:

>Может нужно просто перебрать все возможные комбинации элементов из массива m?

Да, все верно вот так оно объясняется, именно это и надо, но что-то сразу не осилил в условии написать. :D

Хочется сделать с ТСом что-нибудь страшное.

geekless ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.