:: Игры Разума
Re: Можно ли оптимизировать?
medstrax
Забанен

Сообщений: 5964
Дата регистрации: 23.03.2007
Простым циклом вроде никак, рекурсией без труда.
Ratings: 0 negative/0 positive
Re: Можно ли оптимизировать?
Igor Korolyov

Сообщений: 34580
Дата регистрации: 28.05.2002
Да нет, и циклом без проблем. Только смысла в полном переборе мало. Тут он не ищет суммы, просто перебирает все варианты, но печатает всегда лишь выборку из 16 штук (для оценки скорости перебора). Если задать размер массива в 4 то как раз и получим все искомые выборки.
CLEAR
lnSize = 20
DIMENSION aA(m.lnSize)
FOR ln1 = 1 TO m.lnSize
aA[m.ln1] = m.ln1
ENDFOR
lnSec = SECONDS()
FOR ln1 = 0 TO 2^m.lnSize-1
lcS = ""
FOR ln2 = 1 TO m.lnSize
lcS = lcS + IIF(BITTEST(m.ln1, m.ln2 - 1), "," + TRANSFORM(aA[m.ln2]), "")
ENDFOR
lcS = SUBSTR(m.lcS, 2)
IF m.ln1 % 2^(m.lnSize-4) = 0
? INT(m.ln1), m.lcS, "sum is", EVALUATE(EVL(CHRTRAN(m.lcS,",","+"),"0"))
ENDIF
ENDFOR
? "time:", SECONDS() - m.lnSec
Уже для 20 элементов время составит порядка 40 секунд (ну у меня так), а для максимального в данной реализации размера в 32 элемента - уже 45 часов (почти 2-е суток). Тогда как реализация указанного мной приближённого алгоритма (без его особой оптимизации - тупо на фоксовых курсорах реализованный "буквально" алгоритм, да ещё и с хранением "состава набора" в мемо-поле ) работает с ГОРАЗДО большими массивами чисел. Другое дело, что он крайне чувствителен к тому какая именно сумма требуется (и опосредовано этим - какие именно числа будут в массиве), и какая "точность поиска" задана.
Например для набора из 200 случайных целых чисел 1+INT(RAND()*1000), поиска суммы 5000 и точности поиска в 1% (т.е. по сути алгоритм генерирует некоторый набор решений в диапазоне от 4950 до 5000, хотя на практике в него обычно попадает и "абсолютно точное решение", т.к. оно, как правило, существует для такого набора чисел) время завершения составляет порядка 16 секунд. Да, алгоритм может и не найти "абсолютно точное" решение, даже если оно и существует. И он очень сильно "просядет" по времени, если в наборе будут не целые, а вещественные числа (в таком случае лучше их заранее округлить, а потом из найденных решений подобрать то, которое не нарушит условие <= искомая_сумма).


------------------
WBR, Igor
Ratings: 0 negative/0 positive
Re: Можно ли оптимизировать?
medstrax
Забанен

Сообщений: 5964
Дата регистрации: 23.03.2007
Этот код работает неправильно. Если задать lnSize = 5, то в выводе нет например суммы "1,3,5"
Ratings: 0 negative/0 positive
Re: Можно ли оптимизировать?
travelFox
Автор

Сообщений: 344
Откуда: Червоноград
Дата регистрации: 22.02.2011
Исходя из вышесказаного (о времени перебора) я думаю упрощу свой алгоритм к следующему виду:

1) Сортирую элементы массива значений по убыванию.
2) Беру первый элемент, если он больше "предела" тогда отбрасываю.
3) Беру первый подходящий и добавляю к нему первый же попавшийся подходящий по спису снизу.
4) И так пока не доберусь до "предела".

Изьянов куча зато хоть знаю как сделать.
Главная проблема в том, что если взяв, первое попавшееся большое подходящее число, потом можно и не подобрать к нему пару не превысив "предел". А вот с элементов в конце списка верояно можна было бы подобрать и вариант получше.

Эвристикой попахивает. Я бы проще глазами всё увидел, чем машинный перебор.



Исправлено 1 раз(а). Последнее : travelFox, 21.10.11 11:48
Ratings: 0 negative/0 positive
Re: Можно ли оптимизировать?
Igor Korolyov

Сообщений: 34580
Дата регистрации: 28.05.2002
Я же написал, что специально ограничил вывод только 16-ю значениями, чтобы можно было тестировать время перебора для больших размерностей - хочешь видеть все, убери IF (но тогда печать вариантов и прокрутка экрана будут просто дикими для размеров больше 8-10).
2 travelFox
Поскольку раздел не основной, я и не стал сразу приводить реализацию алгоритма поиска из википедии (т.к. это неспортивно ). Вечерком могу выложить.


------------------
WBR, Igor
Ratings: 0 negative/0 positive
Re: Можно ли оптимизировать?
Рома

Сообщений: 1079
Дата регистрации: 06.06.2001
travelFox
Исходя из вышесказаного (о времени перебора) я думаю упрощу свой алгоритм к следующему виду:
1) Сортирую элементы массива значений по убыванию.
2) Беру первый элемент, если он больше "предела" тогда отбрасываю.
3) Беру первый подходящий и добавляю к нему первый же попавшийся подходящий по спису снизу.
4) И так пока не доберусь до "предела".

Это один из алгоритмов решения NP-сложных задач - по науке это называется жадный (greedy) алгоритм со стратегией "первый подходящий" (first fit strategy)

Исходя из логики задачи я бы его улучшил таким образом (стратегия best fit)

1) Сортирую элементы массива значений по убыванию.
2) Берем первые A элементов и записываем их в массив результатов R
3) Вычисляем сумму массива S и ошибку (разницу между S и B)
4) Перебираем элементы массива значений начиная с A+1 элемента = x
а) Для каждого элемента R пытаемся вместо него подставить x
б) Если замена элемента R(i) на х дает лучшую сумму (в смысле ошибки), то запомним его индекс i
в) Если после мы нашли i, то R(i) = x, S = S - R(i) + x
5) Возвращаем R

Сортировка массива значений по возрастанию может дать другой результат (как лучше так и хуже) - можно выполнить
алгоритм и для этого варианта и потом выбрать лучший из двух результатов.
В любом случае это будет быстрее полного перебора возможных вариантов - понятное дело цена за это - всего лишь приблизительный результат



Исправлено 2 раз(а). Последнее : Рома, 21.10.11 16:11
Ratings: 0 negative/0 positive
Re: Можно ли оптимизировать?
Igor Korolyov

Сообщений: 34580
Дата регистрации: 28.05.2002
Вот потому и интересно было бы от автора темы увидеть некоторый генератор начальных условий (не суть важно - в массиве, или в курсоре, важны единые параметры - в частности RAND(1) или нечто подобное для заполнения - чтобы каждый раз на любой машине получалися одинаковые начальные условия).
Вот потом и можно будет заняться реализацией разнообразных алгоритмов - и сравнением их скорости/точности/красивости


------------------
WBR, Igor
Ratings: 0 negative/0 positive
Re: Можно ли оптимизировать?
Рома

Сообщений: 1079
Дата регистрации: 06.06.2001
Ну вот моя реализация - случайный поиск с использованием стратегии по улучшению результата, которую я описывал выше.
Также там есть обычный brute force - для сравнения.

Фокса под рукой сейчас нет - на C# написано.
Ratings: 0 negative/0 positive
Re: Можно ли оптимизировать?
Igor Korolyov

Сообщений: 34580
Дата регистрации: 28.05.2002
Роман, но это не та задача - это именно что "выбрать РОВНО A элементов из массива размерностью N". А автор уже уточнил, что на самом деле ему нужно "выбрать ЛЮБОЕ число элементов из массива размерностью A" - это очень близко к формулировке задачи о сумме подмножества. Единственное существенное отличие - нужно не просто проверить факт существования такого подмножества для ТОЧНОЙ суммы, а найти элементы составляющие подмножество с суммой МЕНЬШЕ ИЛИ РАВНОЙ заданной сумме, но, конечно же, пытаясь максимизировать искомую сумму - в идеале выходя на "точную" сумму.
При том очень существенным для выбора алгоритма является какие именно числа в массиве, сколько их там и какая именно "сумма" требуется - для вещественных, если их не округлять и не приводить таким образом к некоторым "масштабированным целым", или просто для "больших целых" и алгоритм динамического программирования, и предложенный мной алгоритм приближённого поиска потребует кучу памяти - для "динамического" алгоритма, например, потребуется массив размером n*s, где n это число элементов, а s - искомая сумма. Если же в массиве есть и отрицательные числа, то наверное придётся расширять s до размера SUM(положительных) + ABS(SUM(отрицательых)). Для алгоритма приближённого поиска требуется всего 1 большой "набор" (он же массив), но его размер приближается к s, где s это искомая сумма. Т.е. для поиска элементов на сумму 150 потребуется существенно меньше и памяти и времени, чем для поиска элементов на сумму 150000.


------------------
WBR, Igor
Ratings: 0 negative/0 positive
Re: Можно ли оптимизировать?
Рома

Сообщений: 1079
Дата регистрации: 06.06.2001
Igor Korolyov
Роман, но это не та задача - это именно что "выбрать РОВНО A элементов из массива размерностью N". А автор уже уточнил, что на самом деле ему нужно "выбрать ЛЮБОЕ число элементов из массива размерностью A"

"ЛЮБОЕ число элементов" - это, я так понимаю, любое ПОДМНОЖЕСТВО элементов?

Тогда берем A = 1..N, где A - размер подмножества, N-размер исходного массива.
То есть, для решения второй задачи нужно максимум N раз решить первую - из последовательности этих решений мы сможем выбрать лучшее.
Значит, если мы можем найти (хотя бы приближенно) решение первой задачи за полиномиальное время, то и вторая будет решаться тоже за полиномиальное время.

Если же под "ЛЮБОЕ число элементов" подразумевается новое множество, в которое элементы исходного множества могут входить
ЛЮБОЕ число раз - тогда все равно можно свести эту задачу к задаче о подмножествах, так как "ЛЮБОЕ число раз", подозреваю,
конечное число (в реальной жизни по другому не бывает - все ресурсы ограничены).
Ratings: 0 negative/0 positive
Re: Можно ли оптимизировать?
Igor Korolyov

Сообщений: 34580
Дата регистрации: 28.05.2002
Да, любое подмножество.

Да, в википедии описано псевдо-полиноминальное решение методом динамического программирования - его же реализации я видел на rsdn (на паскале ) и ещё где-то. Плюс полиномиальное "приближённое" решение, плюс ссылка на некое "For the case that each xi is positive and bounded by the same constant, Pisinger found a linear time algorith".

Полное решение перебором потребует проверки 2^N вариантов, что непрактично для больших N (для фокса уже размер 20 хорошо тормозит, для шарпа, думаю, можно чуть побыстрее сделать, но всё одно размеры исходного массива в 50-100 будут неподъёмными)...


------------------
WBR, Igor
Ratings: 0 negative/0 positive
Re: Можно ли оптимизировать?
Igor Korolyov

Сообщений: 34580
Дата регистрации: 28.05.2002
В общем раз более желающих нема, приведу фоксовую реализацию вышеупомянутого алгоритма
RAND(1) && Всегда генерим одинаковые последовательности
lnSize = 200
lnSum = 5000 && От величины искомой суммы очень сильно зависит время
lnC = 0.01 && от заданной "точности поиска" очень сильно зависит время
lnDelta = m.lnC * m.lnSum / m.lnSize
CREATE CURSOR cA (a B(0))
FOR ln1 = 1 TO m.lnSize
INSERT INTO cA (a) VALUES (1+INT(RAND() * 1000))
ENDFOR
SET TALK OFF
SET NOTIFY OFF
SET NOTIFY CURSOR OFF
lnSec = SECONDS()
* От способа упорядочения исходных данных
* зависит результат - либо "много мелких", либо "мало крупных"
* но формально алгоритму упорядочение этого массива вообще не нужно
* на скорость оно влияет в зависимости от искомой суммы
* и того какие элементы в массиве - мелкие или крупные
* т.е. в общем случае сказать будет ли быстрее работать
* с индексом по возрастанию, или по убыванию, или вовсе без индекса нельзя
INDEX ON a TAG a &&DESCENDING
CREATE CURSOR cS (s B(0), c M)
INSERT INTO cS (s, c) VALUES (0, "")
SELECT cA
SCAN ALL FOR cA.a <= m.lnSum
SELECT cS.s u, cS.c FROM cS ;
UNION ALL ;
SELECT cA.a + cS.s, cS.c + "," + TRANSFORM(cA.a) FROM cS ;
INTO CURSOR cU
INDEX ON u TAG u
CREATE CURSOR cS (s B(0), c M)
GO TOP IN cU
lnY = cU.u
INSERT INTO cS (s, c) VALUES (m.lnY, cU.c)
SELECT cU
SCAN ALL FOR cU.u > m.lnY + m.lnDelta WHILE cU.u <= m.lnSum
lnY = cU.u
INSERT INTO cS (s, c) VALUES (m.lnY, cU.c)
* Эти "ранные выходы" формально не предусмотрены алгоритмом,
* но могут очень хорошо помочь, если в процессе перебора
* таки будет найдена "точно" искомая сумма.
* IF m.lnY = m.lnSum
* EXIT
* ENDIF
ENDSCAN
*IF m.lnY = m.lnSum
* EXIT
*ENDIF
ENDSCAN
* Просто для наглядности считаем из скольких элементов
* составлена каждая сумма
SELECT Cs.s, Cs.c, OCCURS(",",Cs.c) FROM cS ;
WHERE s BETWEEN (1 - m.lnC) * m.lnSum AND m.lnSum ;
INTO CURSOR res
? SECONDS() - m.lnSec
* Собственно "самое лучшее" будет в последней записи
* для этого примера находит "точную сумму", но при смене параметров
* может не находить точную, но найдёт приближённую
BROWSE LAST
* А вот и теория из википедии
*for each i from 1 to N do
* let T be a list consisting of xi + y, for all y in S
* let U be the union of T and S
* sort U
* make S empty
* let y be the smallest element of U
* add y to S
* for each element z of U in increasing order do
* //trim the list by eliminating numbers close to one another
* //and throw out elements greater than s
* if y + cs/N < z <= s, set y = z and add z to S
* if S contains a number between (1 - c)s and s, output yes, otherwise no


------------------
WBR, Igor
Ratings: 0 negative/0 positive
Re: Можно ли оптимизировать?
PaulWist

Сообщений: 14614
Дата регистрации: 01.04.2004
Игорь.

Поясни смысл:

SCAN ALL FOR cA.a <= m.lnSum

или это надо понимать как "описка".


------------------
Есть многое на свете, друг Горацио...
Что и не снилось нашим мудрецам.
(В.Шекспир Гамлет)
Ratings: 0 negative/0 positive
Re: Можно ли оптимизировать?
travelFox
Автор

Сообщений: 344
Откуда: Червоноград
Дата регистрации: 22.02.2011
Преклоняюсь перед Вашим трудолюбием. Читаю-изучаю Ваш труд.
Ratings: 0 negative/0 positive
Re: Можно ли оптимизировать?
Igor Korolyov

Сообщений: 34580
Дата регистрации: 28.05.2002
Та я ж писал - это "на коленке" писанный код. Конечно ALL там не нужен, т.к. он и так по умолчанию подразумевается для SCAN. Вообще этой "проверки" в алгоритме не предусмотрено, просто она вполне логична - зачем шерстить элементы которые априори "неподходят". Да и индекс вместо ORDER BY из той-же оперы... "Подпилить" в этом коде есть чего

"Трудолюбия", кстати, этот код особого не требует - просто буквально переводится алгоритм (представляя курсоры как наборы/массивы/списки), потом по минимуму сводится в 1 команду то что ранее было как 2-3-4 разных команды (IF переходит в FOR условие, создание набора + слияние с другим - в один SELECT ... UNION) и т.п. Там даже имена курсоров сохранены как в описании алгоритма Ну а дописать кусочек который помимо суммирования ещё и запоминает собственно элементы в memo-поле - дело техники.


------------------
WBR, Igor
Ratings: 0 negative/0 positive
Re: Можно ли оптимизировать?
Рома

Сообщений: 1079
Дата регистрации: 06.06.2001
Для полноты картины добавил к тесту Игоря еще один из алгоритмов, использующих динамическое программирование.

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

Переводил его из C (ссылка на оригинал - в коментариях) - надеюсь, что все правильно.
Ratings: 0 negative/0 positive
Re: Можно ли оптимизировать?
travelFox
Автор

Сообщений: 344
Откуда: Червоноград
Дата регистрации: 22.02.2011
Вобщем я намутил следующее. Не знаю как там на счет эфективности но как-то с горем пополам работает.

predelSuma=VAL(alltrim(ThisForm.Text2.Value))
STORE 1000000 TO predel_old
DIMENSION y[100]
SELECT masiv.num_ber FROM masiv WHERE masiv.num_ber<predelSuma+1 ORDER BY num_ber DESC INTO ARRAY x
razmerMasiva = ALEN(x)
FOR counter=1 TO razmerMasiva
STORE predelSuma TO predel
STORE 1 TO j
FOR i=counter TO razmerMasiva
IF x>predel
LOOP
ELSE
y[j]=x[i]
predel=predel-x[i]
j=j+1
ENDIF
IF predel<=predel_old
ACOPY(y, res)
predel_old=predel
ENDIF
ENDFOR
ENDFOR

И ещё вопрос: при выводе результата пишет все 100 елементов массива даже те у которых нет чисельного значение (.F.). Следующий мой код матерится что данные не совпадают.

t=ALEN(res)
FOR h=1 TO t
IF res[h]==.F.
LOOP
ENDIF
? res[h]
ENDFOR



[i]Исправлено 11 раз(а). Последнее : travelFox, 17.11.11 00:12
Ratings: 0 negative/0 positive


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

On-line: 2 (Гостей: 2)

© 2000-2024 Fox Club 
Яндекс.Метрика