|
Компьютерный форум OSzone.net » Программирование, базы данных и автоматизация действий » Программирование и базы данных » Поиск элементов массива, чья сумма равна заданному числу |
|
|
Поиск элементов массива, чья сумма равна заданному числу
|
Старожил Сообщения: 155 |
Может кто знает, как реализовать такую штуку: есть массив чисел, необходимо найти такие элменты этого массива, чтобы сумма этих чисел равнялась определенному значению (либо установить, что такой комбинации элемнтов массива нет). Интересует алгоритм или какие-то общие соображения на эту тему. Я делаю так (для комбинации из, к примеру, 4 чисел): цикл по всем возможным комбинациям. Также еще один очень важный вопрос: это (см. код) действительно все возможные комбинации?
int NUM[] = {10,20,30,40,50, <...>}; int COUNT = sizeof(NUM)/sizeof(int); int SUM = 35; int i,j,k,n; <...> for(i=0;i<COUNT;i++) for(j=0;j<COUNT;j++) for(k=0;k<COUNT;k++) for(n=0;n<COUNT;n++) { if((i!=j && i!=k && i!=n) && (j!=k && j!=n) && (k!=n)) { if(NUM[i] + NUM[j] + NUM[k] + NUM[n] == SUM) printf("%d %d %d %d\n",i,j,k,n); } } |
|
Отправлено: 00:18, 14-09-2006 |
Ночной странник Сообщения: 4050
|
Профиль | Сайт | Отправить PM | Цитировать ivank
собственно ваш алгоритм не всегда корректно работат... Всё хорошо, путь: 10 2 3 4 5 6 7 8 9 11 12 13 14 15 16 17 18 19 20 = 400 int l = 20; summ2(m,l, l*l); |
------- Отправлено: 16:25, 24-09-2006 | #21 |
Для отключения данного рекламного блока вам необходимо зарегистрироваться или войти с учетной записью социальной сети. Если же вы забыли свой пароль на форуме, то воспользуйтесь данной ссылкой для восстановления пароля. |
редкий гость Сообщения: 1696
|
Профиль | Сайт | Отправить PM | Цитировать Vlad Drakula
Влад. Ты неправильно переделал мой код в C#. И не взял мою исправленную функцию из предыдущего поста. Давай помедитируем над разницей вот этого: Цитата ivank:
Цитата Vlad:
Ну и вот очередной тест. Сортировка не поможет. У меня всё те же несколько секунд. (Впредь прошу при тестах моего кода брать мою версию, а не то, что ты криво сконвертировал). Всего 100 чисел. мой код - 0.08с, понятно что зависимость от l - квадратичная, так что при 1000 числах (в 10 раз больше) уже будет ~10с (в 100 раз дольше). Вуаля: using System; namespace summ { class Class1 { static bool su(int[] m, int[] s, int i, int p, int l, int size, int corsize) { for(;i < l; i++) { if(corsize + m[i] == size) { for(int j = 0; j <= p; j ++) { Console.Write("{0} + ", m[s[j]]); } Console.WriteLine("{0} = {1};", m[i], size); return true; } else if(corsize + m[i] < size) { s[p + 1] = i; if(su(m, s, i + 1, p + 1, l, size, corsize + m[i])) { return true; } } } return false; } static void QSort(int[] ar, int size) { int left, right, ixLeft, ixRight; int[] borderLeft = new int[100]; int[] borderRight = new int[100]; int stach = 0; int copy; int x; Random r = new Random(); borderLeft[0] = 0; borderRight[0] = size - 1; while(stach >= 0) { ixLeft = left = borderLeft[stach]; ixRight = right = borderRight[stach]; stach--; while(left < right) { x = ar[r.Next(left, right)]; ixLeft = left; ixRight = right; while(ixLeft <= ixRight) { while(ar[ixLeft] > x) { ixLeft++; } while(ar[ixRight] < x) { ixRight--; } if(ixLeft <= ixRight) { copy = ar[ixLeft]; ar[ixLeft] = ar[ixRight]; ar[ixRight] = copy; ixLeft++; ixRight--; } } if(ixRight - left < right - ixLeft) { stach++; borderLeft[stach] = ixLeft; borderRight[stach] = right; right = ixRight; } else { stach++; borderLeft[stach] = left; borderRight[stach] = ixRight; left = ixLeft; } } } } static void summ(int[] m, int l, int size) { QSort(m, l); int[] s = new int[l]; su(m, s, 0, -1, l, size, 0); } static void summ2(int[] num, int n, int m) { int[] way = new int[m+1]; for (int i = 0; i <= m; ++i) way[i] = 0; way[0] = 1; for (int i = 0; i < n; ++i) for (int j = m; j > 0; --j) if (way[j] == 0 && j >= num[i] && way[j-num[i]] != 0) way[j] = num[i]; if (way[m] == 0) Console.WriteLine("Всё плохо"); else { Console.Write("Всё хорошо, путь: "); for (int i = m; i != 0; i -= way[i]) Console.Write("{0} ", way[i]); Console.WriteLine(""); } } static void Main(string[] args) { DateTime d1, d2; int l = 100; int[] m = new int[l]; int needed = 0; for(int j = 0; j < l; j++) { m[j] = l+j+1; needed += m[j]; } needed -= m[l/2]; d1 = DateTime.Now; summ2(m, l, needed); d2 = DateTime.Now; Console.WriteLine("ivank's time: {0}", d2 - d1); d1 = DateTime.Now; summ(m, l, needed); d2 = DateTime.Now; Console.WriteLine("vlad's time: {0}", d2 - d1); } } } Честно гворя, я считал, что в СпбГУ достаточно высокий уровень образования, что бы знать комбинаторику в объёме достаточном для понимания, что для перебора всех комбинаций из n чисел требуется 2^n операций. И что это очень большое число уже при n порядка 30. |
||
------- Последний раз редактировалось ivank, 24-09-2006 в 21:47. Отправлено: 20:31, 24-09-2006 | #22 |
Ночной странник Сообщения: 4050
|
Профиль | Сайт | Отправить PM | Цитировать ivank
приношу извинения за недогляд при портировании... предлагаю перейти в тестах на рандомные наборы чисел...
ivank's time: 00:00:11.3763584 vlad's time: 00:00:00.0200288 |
------- Отправлено: 00:38, 25-09-2006 | #23 |
редкий гость Сообщения: 1696
|
Профиль | Сайт | Отправить PM | Цитировать Vlad Drakula
Цитата:
Я показал, что твой алгоритм в некоторых случаях подвисает (например, когда требуемой комбинации не существует. А такое в оригинальной поставновке задачи может быть). Этого достаточно, чтобы его нельзя было применять. Мой алгоритм [i]гарантированно[i] работает всегда за O(m*n), что в оригинальной постановке задачи (n ~ 40, m ~300000) означает не более чем секундную задержку. Секундная задержка лучше, чем возможное подвисание. Более того. Предлагаю просто протестировать на реальных данных оба метода. Реальные данные - пример от автора, в посте номер 8. Цитата:
Аналогично мой метод ограничен произведением m*n. Но как раз в исходной постановке - это не проблема. А вот возможное отсутствие требуемой комбинации -- очень даже. using System; namespace summ { class Class1 { static int su(int[] m, int[] s, int i, int p, int l, int size, int corsize) { for(int r = 0;i < l; i++, r++) { if(corsize + m[i] == size) { for(int j = 0; j <= p; j ++) { Console.Write("{0} + ", m[s[j]]); } Console.WriteLine("{0} = {1};", m[i], size); return 0; } else if(corsize + m[i] < size) { if(i + 1 == l) { return r == 0 ? -1 : 1; } s[p + 1] = i; switch(su(m, s, i + 1, p + 1, l, size, corsize + m[i])) { case 0: return 0; case -1: if(r == 0) { return -1; } break; case 1: r = 1; break; } } else if(i + 1 == l) { return 1; } } return 1; } static void QSort(int[] ar, int size) { int left, right, ixLeft, ixRight; int[] borderLeft = new int[100]; int[] borderRight = new int[100]; int stach = 0; int copy; int x; Random r = new Random(); borderLeft[0] = 0; borderRight[0] = size - 1; while(stach >= 0) { ixLeft = left = borderLeft[stach]; ixRight = right = borderRight[stach]; stach--; while(left < right) { x = ar[r.Next(left, right)]; ixLeft = left; ixRight = right; while(ixLeft <= ixRight) { while(ar[ixLeft] > x) { ixLeft++; } while(ar[ixRight] < x) { ixRight--; } if(ixLeft <= ixRight) { copy = ar[ixLeft]; ar[ixLeft] = ar[ixRight]; ar[ixRight] = copy; ixLeft++; ixRight--; } } if(ixRight - left < right - ixLeft) { stach++; borderLeft[stach] = ixLeft; borderRight[stach] = right; right = ixRight; } else { stach++; borderLeft[stach] = left; borderRight[stach] = ixRight; left = ixLeft; } } } } static bool summ(int[] m, int l, int size) { QSort(m, l); int[] s = new int[l]; return su(m, s, 0, -1, l, size, 0) == 0 ? true : false; } static bool summ2(int[] num, int n, int size) { int m = size; int[] way = new int[m+1]; for (int i = 0; i <= m; ++i) way[i] = 0; way[0] = 1; for (int i = 0; i < n; ++i) for (int j = m; j > 0; --j) if (way[j] == 0 && j >= num[i] && way[j-num[i]] != 0) way[j] = num[i]; if (way[m] == 0) { Console.WriteLine("Всё плохо"); return false; } Console.Write("Всё хорошо, путь: "); for (int i = m; i != 0; i -= way[i]) { Console.Write("{0} ", way[i]); } Console.WriteLine(" = {0}", size); return true; } static void Main(string[] args) { DateTime d1, d2; int l = 42; int[] m = { 197900,70584,65006,39893,27638, 29448,17220,7750,39032,2870, 2010,22386,93275,20090,10906, 36162,4133,40180,64575,34440, 6027,8754,22960,4879,22960, 18311,39893,27638,58896,24108, 22386,2727,36162,4879,4133, 34440,5740,2727,15355,1665, 34440,8610 }; int needed = 350000; // Попробуй 349963 d1 = DateTime.Now; summ2(m,l, needed); d2 = DateTime.Now; Console.WriteLine("ivank's time: {0}", d2 - d1); d1 = DateTime.Now; summ(m,l, needed); d2 = DateTime.Now; Console.WriteLine("vlad's time: {0}", d2 - d1); } } } |
||
------- Отправлено: 01:14, 25-09-2006 | #24 |
редкий гость Сообщения: 1696
|
Профиль | Сайт | Отправить PM | Цитировать P.P.S. последний кусочек статистики.
int[] m = { 197900,70584,65006,39893,27638, 29448,17220,7750,39032,2870, 2010,22386,93275,20090,10906, 36162,4133,40180,64575,34440, 6027,8754,22960,4879,22960, 18311,39893,27638,58896,24108, 22386,2727,36162,4879,4133, 34440,5740,2727,15355,1665, 34440,8610 }; Максимальная сумма - 1233188. При этом невозможно получить 306002 различных сумм с помощью данного набора. То есть, если выбирать needed случайно, с вероятностью 1/4 твоя программа зависнет. using System; namespace summ { class Class1 { static int su(int[] m, int[] s, int i, int p, int l, int size, int corsize) { for(int r = 0;i < l; i++, r++) { if(corsize + m[i] == size) { for(int j = 0; j <= p; j ++) { Console.Write("{0} + ", m[s[j]]); } Console.WriteLine("{0} = {1};", m[i], size); return 0; } else if(corsize + m[i] < size) { if(i + 1 == l) { return r == 0 ? -1 : 1; } s[p + 1] = i; switch(su(m, s, i + 1, p + 1, l, size, corsize + m[i])) { case 0: return 0; case -1: if(r == 0) { return -1; } break; case 1: r = 1; break; } } else if(i + 1 == l) { return 1; } } return 1; } static void QSort(int[] ar, int size) { int left, right, ixLeft, ixRight; int[] borderLeft = new int[100]; int[] borderRight = new int[100]; int stach = 0; int copy; int x; Random r = new Random(); borderLeft[0] = 0; borderRight[0] = size - 1; while(stach >= 0) { ixLeft = left = borderLeft[stach]; ixRight = right = borderRight[stach]; stach--; while(left < right) { x = ar[r.Next(left, right)]; ixLeft = left; ixRight = right; while(ixLeft <= ixRight) { while(ar[ixLeft] > x) { ixLeft++; } while(ar[ixRight] < x) { ixRight--; } if(ixLeft <= ixRight) { copy = ar[ixLeft]; ar[ixLeft] = ar[ixRight]; ar[ixRight] = copy; ixLeft++; ixRight--; } } if(ixRight - left < right - ixLeft) { stach++; borderLeft[stach] = ixLeft; borderRight[stach] = right; right = ixRight; } else { stach++; borderLeft[stach] = left; borderRight[stach] = ixRight; left = ixLeft; } } } } static bool summ(int[] m, int l, int size) { QSort(m, l); int[] s = new int[l]; return su(m, s, 0, -1, l, size, 0) == 0 ? true : false; } static bool summ2(int[] num, int n, int size) { int m = size; int[] way = new int[m+1]; for (int i = 0; i <= m; ++i) way[i] = 0; way[0] = 1; for (int i = 0; i < n; ++i) for (int j = m; j > 0; --j) if (way[j] == 0 && j >= num[i] && way[j-num[i]] != 0) way[j] = num[i]; if (way[m] == 0) { Console.WriteLine("Всё плохо"); return false; } Console.Write("Всё хорошо, путь: "); for (int i = m; i != 0; i -= way[i]) { Console.Write("{0} ", way[i]); } Console.WriteLine(" = {0}", size); return true; } static void Main(string[] args) { DateTime d1, d2; int l = 42; int[] m = { 197900,70584,65006,39893,27638, 29448,17220,7750,39032,2870, 2010,22386,93275,20090,10906, 36162,4133,40180,64575,34440, 6027,8754,22960,4879,22960, 18311,39893,27638,58896,24108, 22386,2727,36162,4879,4133, 34440,5740,2727,15355,1665, 34440,8610 }; Random r = new Random(); while (true) { int needed = r.Next(1665, 1233188); d1 = DateTime.Now; summ2(m,l, needed); d2 = DateTime.Now; Console.WriteLine("ivank's time: {0}", d2 - d1); d1 = DateTime.Now; summ(m,l, needed); d2 = DateTime.Now; Console.WriteLine("vlad's time: {0}", d2 - d1); } } } } |
------- Отправлено: 01:29, 25-09-2006 | #25 |
![]() Ветеран Сообщения: 1180
|
Профиль | Отправить PM | Цитировать Постановка задачи про рюкзак (bounded knapsack problem) дико напоминает линейное программирование. Или я не прав? (смотрел ссылку, они решили как задачу дискретного оптимального управления)
|
Отправлено: 08:04, 25-09-2006 | #26 |
редкий гость Сообщения: 1696
|
Профиль | Сайт | Отправить PM | Цитировать pva
Честно говоря, не очень представляю как это можно сделать. Все известные мне методы линейного программирования, которые ищут дискретные коэфициенты, в конечном счёте NP-полные (читай: сводятся всё к тому же перебору). Кстати, нашёл описание именно нашего частного случая на википедии: http://en.wikipedia.org/wiki/Subset_sum_problem . Там прямо сказано, что это не задача оптимизации (т.е. линейное программирование не применимо. По крайней мере в чистом виде). |
------- Отправлено: 12:13, 25-09-2006 | #27 |
Ночной странник Сообщения: 4050
|
Профиль | Сайт | Отправить PM | Цитировать ivank
соглашусь что ваш алгоритм для данной задачи (типов наборов чисел) лучьше...(пока я не придумал алгорим быстрее) но веть и быстрая сортировка имеет шанс работать по n*n, и ничего пользуются... + еще одно но: а как на счет реализации? если по оптимизировать то можно сократить время работы в 3-7 раз... ![]() |
------- Отправлено: 01:42, 26-09-2006 | #28 |
редкий гость Сообщения: 1696
|
Профиль | Сайт | Отправить PM | Цитировать Vlad Drakula
Цитата:
Аналогично в среднем случае твой алгоритм значительно лучше. Вот только на предполагаемых наборах чисел (25-50) вероятность влететь в худший случай максимальна (т.к. чем больше чисел (n>50), тем больше вероятность получить требуемую сумму с помощью твоей эвристики. А при малом n (<25), даже если требуеммую сумму получить нельзя, стоимость полного перебора невелика). Цитата:
|
||
------- Отправлено: 03:31, 26-09-2006 | #29 |
|
![]() |
Участник сейчас на форуме |
![]() |
Участник вне форума |
![]() |
Автор темы |
![]() |
Сообщение прикреплено |
| |||||
Название темы | Автор | Информация о форуме | Ответов | Последнее сообщение | |
Удаление записей из таблицы по заданному времени на php | magwolf | Вебмастеру | 5 | 14-08-2009 14:13 | |
[решено] Чему равна скорость 1МБбит/сек ? | Rock | Хочу все знать | 12 | 30-09-2008 04:50 | |
GF8600gts Asus или др. чья лучше? | Ochotnik | Видеокарты | 5 | 20-03-2008 19:12 | |
С/С++ | Выбор 10 случайных элементов из массива | Vovius | Программирование и базы данных | 5 | 29-08-2006 19:37 | |
Решено | XML. DOM. Поиск дочерних элементов. | penykov | Программирование и базы данных | 3 | 27-04-2006 15:46 |
|