цифры реальные с монитора порта...почему у коляна несколько не те - не знаю...ядро минисоре оптимизация включена
Со временем выполнения тут есть некая странность. Я уже описывал в #154. Кроме того, проверял код АндреяД, в 1.8.7 исполняется дольше (мой, наоборот, быстрее). А в 1.8.13 - код АндреяД у меня и у него выполняются за одинаковое время.
Андрей! это ПАТАМУШТА задачу решить пытаешься чисто математически, а она всё таки прикладная, размеры ключей в паре формируются не с потолка, а из ограничений на усилия, то-есть с ключом на 10 не может быть в паре размерность к примеру 17 (и выше) подумай, как это можно обыграть, да никто не мешает тебе сделать массив размерностей (5,6,7...) и его использовать, они стандартизированы и, смотришь, в один проход с дельтой возврата получится реализовать
В задании не сказано, что ключ это именно гаечный ключ. Может это ключ от входной двери с двумя размерами бородок сваренных между собой, ну как вариант. )
Т.е. на входе имеем совокупность объектов под названием "ключ" с тремя свойствами: 1. первый ненулевой размер; 2. второй ненулевой размер не равный первому. 3. цена.
Можно даже подтянуть за уши, что цена это не денежный эквивалент, а, например, затраты времени на использование ключа. И тогда может это составные ключи базы данных, состоящий из двух размеров (длин).
Хех, типа, а вдруг новичок докажет P=NP, просто потому, что не знает об этом?
Поучаствую-ка и я, моё решение вне конкурса и не для ардуины, зато самое быстрое по времени создания - менее 5 минут, решает за 500 мксек, ну правда и цпу пошустрее, чем атмега.
Таким образом, получаем минимальное остовное дерево, которое нам и требовалось. Понравился прикол с размерами ключей 12-13-15 и 12-14-15. В решении 42 строки, что доказывает, что смысл жизни - в знаниях :)
PS Кто не писал алгоритм для упаковки товаров со свойствами Ш*Д*В в коробки доставки Ш*Д*В - жизни не видел. :)
Таким образом, получаем минимальное остовное дерево, которое нам и требовалось.
А Вы думаете,что эта простая и очевидная на первый взгляд, однако неправильная в корне идея осенила только Вас;) Ошибаетесь. Кста и на Ардуине она решается за сопоставимое время, поверьте мне - она у мной написана. Только зто задача не про связный граф, а про множество() графов из двух узлов и одной дуги. И мин. деревом для ЛЮБОГО ключа будет его ЦЕНА. И в верное решение могут входить ключи, которые не входят в остовное, которое Вы изобразили...
Андрей! это ПАТАМУШТА задачу решить пытаешься чисто математически, а она всё таки прикладная, размеры ключей в паре формируются не с потолка, а из ограничений на усилия
а ты все время в чисто программисткую задачу пытаешься внести сермяжный смысл , достойный кладовщика инструментального цеха.
Слушай, мне как програмисту глубоко плевать на размеры по ГОСТу. Программа должна работать с любыми наборами, даже совершенно бредовыми, типа 3х55, иначе это не программа.
И от меня просьба - если тебе нечего сказать по сути конкурса - лучше не говори ничего
Да, у меня получился алгоритм для определённых типов стартовых наборов, сделать его универсальным у меня не получилось.
Андрей, посмотрел код... никак не могу понять, почему вы сделали ключевой характеристикой поиска не цену, а разницу между размерами на концах ключа. Какое отношение это число имеет к оптимальному набору? С чего вы взяли, что ключ 14Х17 обязательно выгоднее 17х19 ?
То, что это (совершенно случайно) оказалось верным для тестового набора - никак не помогает в работе с другими наборами. Поэтому, как мне кажется, ваш алгоритм и не работает.
Думаю, это влияние ua6em... это он задурил вам готову своими ГОСТами и словами, что размеры ключей должны быть строго определенных комбинаций и заданы в соответвии с реальной линейкой размеров... Это неверно. На самом деле по условию размеры могут быть абсолютно любыми..
У меня получился алгоритм для конкретного набора. Разрабатывал его от правильного результата. Т.е. посмотрел какие должны быть выбраны ключи и как их надо выбирать и нашёл такую закономерность.
Интересно, сколько в итоге будет участников, и сколько будет 100% рабочих вариантов.
Жаль ничего не петрю в алгоритмах и математике, тоже бы поучаствовал. Помню, лет 20 назад участвовал в конкурсе программирования игр, Lines была задача написать. Сколько то шаров одноцветных, несколько двуцветных и один универсальный. Интересно было написать алгоритм проверки на 5 одинаковых цветов в линии. Даже выиграл тогда, но уровень соперников не известен, и никто не написал 100% рабочего варианта, а так хотелось сравнить.
А чем мой предпоследний вариант не рабочий? Он только долгий. Да и у kolyn, вроде рабочие варианты.
Свой новый алгоритм хочу ещё потестить, "поиграться" с оптимизацией по времени. До конца конкурса ещё шесть дней, даже если и найду баги, всё равно выложу свой последний код на выходных.
Кроме числа ключей имеет смысл еще поиграться связностью. Например наборы от ua6em - хоть и хороши для "нагрузочного тестирования" - в основном сложны обьемом, а топография у них довольно простенькая, почти линейная. О чем я веду речь?
Вот, к примеру, набор Евгения Петровича:
Видно. что в нем есть ветвления и циклы. Но так же есть и длинные "хвосты", которые сильно облегчают решение.
Добавим к нему всего один ключ - и набор станет совсем другим:
И вот самое интересное - насколько будет отличаться время решения этих двух наборов алгоритмами разных авторов :)
Чисто практически - набор ЕП у меня считается за 2.8 мс, а если добавить к нему ключик со следующими парметрами:
{9, 27, 71570}
то время расчета возрастает сразу до 14.2 мс - ровно в пять раз.
PS Кто не писал алгоритм для упаковки товаров со свойствами Ш*Д*В в коробки доставки Ш*Д*В - жизни не видел. :)
писал такое , в качестве дипломного проэкта . правда не для коробок , а для упаковки "вычислительных модулей на кристалле" в 3D ,и там не было учтено например : темература и поворт кромер как на 90 градусов.
Интересно, сколько в итоге будет участников, и сколько будет 100% рабочих вариантов.
что считать 100% рабочим вариантом? Зависит от того, как тестировать. Если отбросить явные ошибки алгоритмов, то все упирается в память. Памяти в атмеги328 мало. а очевидно что чем больше ключей - тем быстрее она кончается.
На данный момент у меня есть код, который все небольшие наборы из ветки решает влет :) Но наборах от товарища ua6em он затыкается. Вот уже неделю бьюсь, выкинул из кода все лишнее, выиграл в памяти почти в 2 раза - но до успеха еще очень далеко. Например, набор из сообщения #220 требует для решения 12 уровней рекурсии. А память заканчивается на четвертом :( И хотя каждый следующий уровень меньше предыдущего - все-таки разница между 4 и 12-тью - не очень обнадеживающая :)))
С массивом из 100 ключей пустой скелет выдаёт: Глобальные переменные используют 1210 байт (59%).
Как вариант можно переместить included в несколько uint64_t, а оставшийся массив структур в PROGMEM, если, конечно, никаких манипуляций по изменению массива структур код делать не будет.
По моему коду.
Полный ГОСТовский набор у меня за сутки так и не посчитался, если его разделить примерно пополам, обе половины проходят.
Ключи
4. 15-14: 617 руб. 14 коп.
10. 13-17: 0 руб. 17 коп.
11. 13-16: 0 руб. 16 коп.
14. 12-11: 0 руб. 12 коп.
16. 10-11: 0 руб. 10 коп.
17. 9-11: 0 руб. 9 коп.
18. 7-8: 0 руб. 8 коп.
20. 5-3: 0 руб. 5 коп.
23. 4-2: 0 руб. 2 коп.
24. 2-1: 0 руб. 1 коп.
25. 18-19: 0 руб. 2 коп.
26. 20-21: 0 руб. 3 коп.
27. 24-25: 0 руб. 4 коп.
28. 6-7: 0 руб. 7 коп.
29. 26-27: 0 руб. 10 коп.
30. 29-28: 0 руб. 10 коп.
31. 30-31: 514 руб. 55 коп.
32. 32-33: 565 руб. 44 коп.
33. 34-35: 565 руб. 44 коп.
34. 36-37: 565 руб. 44 коп.
35. 39-38: 0 руб. 16 коп.
36. 40-41: 0 руб. 16 коп.
37. 42-43: 0 руб. 16 коп.
Стоимость набора: 2829 руб. 65 коп.
Время расчёта: 162206 миллисекунд
Буду благодарен, если предложите наборы для тестирования, на условиях:
// Ключей в массиве не больше 100
// Размеры в одном ключе целочисленные, не равны 0, не равны между собой, и каждый не больше 255
// При наличии пресекающихся по размерам ключей в кол-ве больше 30 вычисления займут длительное время
Нашел в коде серьезный косяк. Исправленная версия:
//
// Тип данных для хранения ключа
//
struct Spanner : public Printable {
uint32_t price;// цена в копейках
uint8_t size1; // размер 1
uint8_t size2; // размер 2
uint8_t included; // если 1, то включается в результирующий набор,0 - возможно будет включен,10 - исключен из перебора
//ТУТ НЕ ТАК,КАК В ВАС! included МОЖЕТ ПРИНИМАТЬ: если 1 (оn), то включается в результирующий набор,0 (off) - возможно будет включен,
Spanner(const uint8_t s1, const uint8_t s2, const uint32_t p, const uint8_t ini = 0)
: price(p), size1(s1), size2(s2), included(ini) {}
size_t printTo(Print & p) const {
size_t res = p.print(size1);
res += p.print('-');
res += p.print(size2);
res += p.print(": ");
res += printPrice(p, price);
return res;
}
static size_t printPrice(Print & p, const uint32_t priceCop) {
size_t res = p.print(priceCop / 100);
res += p.print(" руб. ");
res += p.print(priceCop % 100);
res += p.print(" коп.");
return res;
}
};
//
// Массив всех ключей
//
static Spanner spanners[] = {
{6, 8 , 38208},
{8, 9 , 41520},
{8, 10, 43054},
{9, 11, 49597},
{10, 12, 51455},
{12, 13, 56544},
{12, 14, 57675},
{13, 15, 62845},
{14, 15, 61714},
{14, 17, 70276},
{16, 17, 76496},
{16, 18, 81989},
{17, 19, 82312},
{18, 19, 82877},
{19, 22, 110826},
{22, 24, 145560},
{24, 27, 171570}
};
static constexpr uint8_t totalSpanners = sizeof(spanners) / sizeof(spanners[0]);
const uint8_t on = 1, off = 0, excluded = 10;// если 1, то включается в результирующий набор,0 - возможно будет включен,10 - исключен из перебора
//
// Эта функция вычисляет оптимальный набор ключей
// и устанваливает поле included в on для ключей,
// которые попадают в решение, и off или excluded для ключей,
// которые не попадают в решение.
//
void doCalculations(void) {
spanSort(spanners); //отсортировали
uint8_t quantityOff; //ввели переменную кол-ва ключей, которые могут быть включены "off" в набор
uint8_t quantityOn; //ввели переменную кол-ва ключей, которые могут быть включены "off" в набор
uint8_t quantitySize = totalSize();//кол-во уникальных размеров и заодно удалили (excluded) дубли
uint8_t minQuantSpann; //ввели переменную миним. кол-во ключей, из которых может состоять набор
if (quantitySize % 2 == 0)
minQuantSpann = quantitySize / 2;
else minQuantSpann = quantitySize / 2 + 1; //Вычислили мин. возможное кол-во ключей
quantityOn = unique(); //включили уникальные 6*8; 9*11; 24*27.
exclud(); //Тут исключать 8*9
quantityOff = offSpanners(); //
if (quantityOff == 0)return;
sortOff(spanners); //Сортировка невклученных офф вперед
//перебор вариантов.Почти все, что выше исключительно для умеьшения возм. сочетаний и времени.
bruteForce(minQuantSpann, quantityOff, quantityOn, quantitySize);
}
//
// Функция печатает результирующий
// набор ключей и его стоимость
//
void printResults(void) {
Serial.println("Ключи");
uint32_t total = 0;
for (uint8_t i = 0; i < totalSpanners; i++) {
if (spanners[i].included == on) {
Serial.print(i + 1);
Serial.print(". ");
Serial.println(spanners[i]);
total += spanners[i].price;
}
}
Serial.print("Стоимость набора: ");
Spanner::printPrice(Serial, total);
Serial.println();
}
//--------------------------------------------------
//
// Функция пeребора вариантов сочетаний ключей
//
void bruteForce(uint8_t minQ, uint8_t qOff, uint8_t qOn, uint8_t qSize)
{
uint8_t tempArr[qOff];
uint32_t tempTotal = 4294967295;
bool complectYes = false;
for ( minQ; minQ <= qOff + qOn; minQ++)
{ //Serial.println(minQ);
bool flagComplect = false;
uint8_t *currArr;
currArr = new uint8_t[minQ - qOn];
for (uint8_t i = 0; i < minQ - qOn; i++) //первое сочетание ключей
{
currArr[i] = i + 1;
}
for (uint8_t i = 0; i < minQ - qOn; i++) //сделали их "on"
spanners[currArr[i] - 1].included = on;
if ( totalSizeOn(qOff, qOn, qSize) == true) //если собрался правильный набор набор
{
uint32_t currTotal = 0;
for (uint8_t i = 0; i < qOff + qOn; i++) //подсчитали цену
{
if (spanners[i].included == on)
currTotal += spanners[i].price;
}
if (currTotal <= tempTotal) //если цена меньше записанной или это первый набор
{
tempTotal = currTotal; //записываем цену
for (uint8_t i = 0; i < qOff; i++) //темп аррей делаем пустым
tempArr[i] = 0;
for (uint8_t i = 0; i < minQ - qOn; i++)
tempArr[currArr[i] - 1] = 1; //записываем в темп аррей
flagComplect = true; //поднимаем флаг "собран комплект"
}
}
for (uint8_t i = 0; i < minQ - qOn; i++) //сбросили ключи в "off"
spanners[currArr[i] - 1].included = off;
while (NextSet(currArr, qOff, minQ - qOn)) //новое сочетание ключей
{
for (uint8_t i = 0; i < minQ - qOn; i++) //сделали их "on"
{
spanners[currArr[i] - 1].included = on;
}
if ( totalSizeOn(qOff, qOn, qSize)) //если собрался правильный набор набор
{
uint32_t currTotal = 0;
for (uint8_t i = 0; i < qOff + qOn; i++) //подсчитали цену
{
if (spanners[i].included == on)
{
currTotal += spanners[i].price;
}
}
if (currTotal <= tempTotal ) //если цена меньше записанной или это первый набор
{
tempTotal = currTotal; //записываем цену
for (uint8_t i = 0; i < qOff; i++) //темп аррей делаем пустым
tempArr[i] = 0;
for (uint8_t i = 0; i < minQ - qOn; i++)
{
tempArr[currArr[i] - 1] = 1; //записываем в темп аррей
}
flagComplect = true; //поднимаем флаг "собран комплект"
}
}
for (uint8_t i = 0; i < minQ - qOn; i++) //сбросили ключи в "off"
spanners[currArr[i] - 1].included = off;
}
delete[] currArr; // динамич. массив удаляем
if (flagComplect == true) // комплект был создан мин.1 раз
complectYes = true;
if (flagComplect == false && complectYes == true) //если в иттерации не нашли комплект дешевле
{ //и комплект был создан ранее выходим
for (uint8_t i = 0; i < qOff; i++)
spanners[i].included = tempArr[i];
break;
}
if ( qOff == minQ + qOn) //Последняя иттер.
{
for (uint8_t i = 0; i < qOff; i++)
spanners[i].included = tempArr[i];
}
}
}
//-------------------------------------------------
//Функция вычисляет количество существующих размеров среди включенных "On"ключей и сравнивает с зна
bool totalSizeOn(uint8_t qOff, uint8_t qOn, uint8_t qSize)
{
uint8_t inSize = 0;
for (uint8_t i = 0; i < qOn + qOff; i++)
{ bool flag1 = false;
bool flag2 = false;
if (spanners[i].included == on)
{
for (int k = i - 1; k >= 0; k--)
{ if (spanners[k].included != on)
{
continue;
}
if (spanners[i].size1 == spanners[k].size1 ||
spanners[i].size1 == spanners[k].size2)
{
flag1 = true;
}
if (spanners[i].size2 == spanners[k].size1 ||
spanners[i].size2 == spanners[k].size2)
{
flag2 = true;
}
}
if (flag1 == false) inSize++;
if (flag2 == false) inSize++;
}
}
if (inSize != qSize)
return false;
else return true;
}
//---------------------------------------------------------
//
//Функция всех сочетаний для чисел 1…n по m.
//Честно содрана на <a href="<a href="<a href="https://prog-cpp.ru/combinations/"
//
bool NextSet(uint8_t *a, uint8_t n, uint8_t m)
{
uint8_t k = m;
for (int i = k - 1; i >= 0; --i)
if (a[i] < n - k + i + 1)
{
++a[i];
for (uint8_t j = i + 1; j < k; ++j)
a[j] = a[j - 1] + 1;
return true;
}
return false;
}
//-----------------------------------------------------
//
//Функция подсчета невключенных(off)ключей
//
uint8_t offSpanners()
{
uint8_t j = 0;
for (uint8_t i = 0; i < (totalSpanners ); i++)
{
if (spanners[i].included == off)
j++;
}
return j;
}
//----------------------------------------
//
// функция сортировки ключей по "off"; ключи со значением included == off станут первыми
//
void sortOff(Spanner * spanarray)
{
Spanner tmp[] = {{0, 0, 0}};
for (uint8_t i = 0; i < (totalSpanners ); i++)
{
for (int j = 0; j < totalSpanners - 1; j++)
{
if ((spanarray[j].included) > (spanarray[j + 1].included))
{
tmp[0] = spanarray[j];
spanarray[j] = spanarray[j + 1];
spanarray[j + 1] = tmp[0];
}
}
}
}
//------------------------------------------------------
//
//Функция исключает из анализа (excluded) ключи, размеры которых дублируют обязательно включенные функцией unique()
//
//
void exclud()
{
uint8_t inSp = 0;
for (uint8_t i = 0; i < totalSpanners; i++)
{
if (spanners[i].included == on) inSp++;
}
inSp = inSp * 2;
uint8_t *arrayTemp = new uint8_t[inSp];
uint8_t j = 0;
for (uint8_t i = 0; i < totalSpanners; i++)
{
if (spanners[i].included == on)
{
arrayTemp[j] = spanners[i].size1;
j++;
arrayTemp[j] = spanners[i].size2;
j++;
}
}
for (uint8_t i = 0; i < totalSpanners; i++)
{
bool flag1 = false;
bool flag2 = false;
if (spanners[i].included == off)
{
for (uint8_t k = 0; k < j; k++)
{
if (spanners[i].size1 == arrayTemp[k] )
{
flag1 = true;
}
if (spanners[i].size2 == arrayTemp[k])
{
flag2 = true;
}
}
}
if (flag1 == true && flag2 == true )
{
spanners[i].included = excluded;
}
}
delete[] arrayTemp;
}
//-----------------------------------------------------------
//
// Функция определяет неповторяющиеся размеры и ключи, которым они принадлежат, "делаем" "on"
//
uint8_t unique()
{
uint8_t uniqueSpanner = 0;
for (uint8_t i = 0; i < totalSpanners; i++)
{
bool flag1 = false;
bool flag2 = false;
for (uint8_t k = 0; k < totalSpanners; k++)
{
if (i == k) continue; //сам на себя
if (spanners[k].included == excluded) continue; //на удаленный
if (spanners[i].size1 == spanners[k].size1 ||
spanners[i].size1 == spanners[k].size2)
{
flag1 = true;
}
if (spanners[i].size2 == spanners[k].size1 ||
spanners[i].size2 == spanners[k].size2)
{
flag2 = true;
}
}
if (flag1 == false || flag2 == false)
{
spanners[i].included = on;
uniqueSpanner++;
}
}
//Serial.print(uniqueSpanner);
return uniqueSpanner;
}
//------------------------------------------
//
//Функция вычисляет общее количество существующих размеров и Функция удаляет (excluded) дубли ключей
//
uint8_t totalSize()
{
int8_t arrSize[totalSpanners * 2] = {};
uint8_t inSize = 0;
uint8_t y = 0;
for (uint8_t i = 0; i < totalSpanners; i++)
{
arrSize[y] = spanners[i].size1;
y++;
arrSize[y] = spanners[i].size2;
y++;
for (int k = i - 1; k >= 0; k--)
{
if ((spanners[i].size1 == spanners[k].size1 ||
spanners[i].size1 == spanners[k].size2) &&
(spanners[i].size2 == spanners[k].size1 ||
spanners[i].size2 == spanners[k].size2))
{
spanners[i].included = excluded;
}
}
}
for (uint8_t i = 0; i < totalSpanners * 2; i++)
{
uint8_t j = 0;
while (j < i && arrSize[j] != arrSize[i])
++j;
if (j == i)
inSize ++;
}
//Serial.print(inSize);
return inSize;
}
//-------------------------------------------------------
//
// функция сортировки ключей от дешевых к дорогим
//
void spanSort(Spanner * spanarray)
{
Spanner tmp[] = {{0, 0, 0}};
for (uint8_t i = 0; i < (totalSpanners ); i++)
{
for (int j = 0; j < totalSpanners - 1; j++)
{
if ((spanarray[j].price) > (spanarray[j + 1].price))
{
tmp[0] = spanarray[j];
spanarray[j] = spanarray[j + 1];
spanarray[j + 1] = tmp[0];
}
}
}
}
//------------------------------------------------------
void setup(void) {
Serial.begin(9600);
const uint32_t start = millis();
doCalculations();
const uint32_t duration = millis() - start;
printResults();
Serial.print("Время расчёта: ");
Serial.print(duration);
Serial.println(" миллисекунд");
}
void loop(void) {}
Результат выполнения в 1.8.13
Ключи
2. 10-12: 514 руб. 55 коп.
6. 13-15: 628 руб. 45 коп.
7. 14-17: 702 руб. 76 коп.
9. 16-18: 819 руб. 89 коп.
12. 19-22: 1108 руб. 26 коп.
14. 6-8: 382 руб. 8 коп.
15. 9-11: 495 руб. 97 коп.
16. 24-27: 1715 руб. 70 коп.
Стоимость набора: 6367 руб. 66 коп.
Время расчёта: 637 миллисекунд
К сожалению в плане оптимизации по времени ничего не выжал. При переборе с пом. "сочетаний без повторений" метод ветвей - границ в полной мере применить не сумел.
Тоже использую сочетания без повторений, но немного по другому. Подсказка. :) Время исполнения моего алгоритма примерно половина от вашего времени. Хотя, может о чём я думаю уже у вас реализовано, не вникал в ваш код.
И ещё появилась идея как ускорить, думаю, за выходные успею до ума довести.
Это вариант "А", так называемый быстрый, без экономии памяти. Для наборов более 20 ключей может вываливаться с сообщениям о недостатке памяти или тихо виснуть (долго ждать не надо, если на экране ничего нет более секунды - значит завис :)
Как видно, скорость работы зависит от числа ключей совсем не однозначно... Строчки выше тестировать не стал
Наборы из 100 ключей не работают.
Проверяйте
Есть еще вариант"Б" - медленный , зато более экономный до ресурсов. Его, если будет интерес, выложу позже. Но большой разницы там нет, например по ГОСТовскому набору максимум для варианта А - 30 ключей, для Б - 34, зато скорость ниже примерно в 5 раз
Моё виденье каким должен быть алгоритм, а вдруг ещё есть молчуны-участники и смогут осуществить за три дня:
Многоуровневая фильтрация массива ключей. (типа несколько уровней сито)
Если фильтрация не справилась, то перебор сочетаниями без повторений.
Возможно, в процессе фильтрации можно разделить набор на поднаборы и уже к ним применять пункт 2.
Или при грамотно продуманной фильтрации перебор и не понадобиться.
Фильтрация:
Дубли с высокой ценой, при этом нужно учесть, что ключи могут быть перевёрнутыми, исключаются сразу.
Ключи, в которых размер встречается только один раз (уникальные ключи), по любому добавляются в набор.
Тут проявляется условие, что у каждого ключа есть два размера не равных между собой. Т.е. на примере набора из задания, который я и буду приводить дальше, 6-8, 9-11, 24-27 уникальные ключи с размерами 6, 11, 27, а размеры 8, 9, 24 уже не нужны для дальнейшей проверки.
При этом ключ 8-9 уже не нужен, т.к. его размеры уже включены в результирующий набор.
Далее могут присутствовать ключи, у которых один размер исключен, можно сравнить его стоимость с остальными ключами с таким же размером и если его цена больше то исключить его. При этом, если кол-во ключей с этим размером был равен 2, то более дешёвый ключ становиться уникальным. А его второй размер исключается из дальнейшего перебора.
Потом можно в цикле повторить пункты 3-5.
Если что-то останется, то перебор сочетаний без повторений.
без 6 пункта уже реализовал, попробую за оставшееся время конкурса успеть.
касаемо вашей идеи насчет п 6, то далеко не в каждом наборе многократный проход дает выигрыш.
Я в сообщении 268 рисовал графы, в исходном наборе ваш п6 выгоден, а "кольцевом" мало что даст
Хотелось бы еще минимальных авторских комментариев к коду, желательно на "могучем". Патамушта не все могут уловить главную сюжетную линию;) Я - так точно;)))
Примерно, как описано у Андрея в 275, только в конце не перебор, а рекурсия на один шаг глубже и снова эти же 7 пунктов... И так пока граф ключей не кончится
Все тесты, которые я придумал или те корректные, что уже были ветке, у меня проходят
Строки 152-204 написал только сегодня, получилось коряво, ну раз работает не стал трогать.
Мой код:
// Тип данных для хранения ключа
struct Spanner : public Printable {
uint32_t price;// цена в копейках
uint8_t size1; // размер 1
uint8_t size2; // размер 2
bool included; // если true, то включается в результирующий набор или дубли
Spanner(const uint8_t s1, const uint8_t s2, const uint32_t p, const bool ini = false)
: price(p), size1(s1), size2(s2), included(ini) {}
size_t printTo(Print & p) const {
size_t res = p.print(size1);
res += p.print(F("-"));
res += p.print(size2);
res += p.print(F(": "));
res += printPrice(p, price);
return res;
}
static size_t printPrice(Print & p, const uint32_t priceCop) {
size_t res = p.print(priceCop / 100);
res += p.print(F(" руб. "));
res += p.print(priceCop % 100);
res += p.print(F(" коп."));
return res;
}
};
// Массив всех ключей
// Ключей в массиве не больше 100
// Размеры в одном ключе целочисленные, не равны 0, не равны между собой, и каждый не больше 255
// При наличии пресекающихся по размерам ключей в кол-ве больше 30 вычисления могут занять длительное время
static Spanner spanners[] = {
{6, 8 , 38208},
{8, 9 , 41520},
{8, 10, 43054},
{9, 11, 49597},
{10, 12, 51455},
{12, 13, 56544},
{12, 14, 57675},
{13, 15, 62845},
{14, 15, 61714},
{14, 17, 70276},
{16, 17, 76496},
{16, 18, 81989},
{17, 19, 82312},
{18, 19, 82877},
{19, 22, 110826},
{22, 24, 145560},
{24, 27, 171570},
};
static constexpr uint8_t totalSpanners = sizeof(spanners) / sizeof(spanners[0]);
uint8_t sizes[totalSpanners * 2], cSizes[totalSpanners * 2];
Spanner tempS[] = {{0,0,0,0}};
bool flag1=0, flag2=0, flag3=0;
uint8_t totalSpanners1=0, allSizes=0;
// Функция перебора сочетаний без повторений
bool nextSet(uint8_t *indexKeys, int16_t n, int16_t m) {
for (int16_t i = m - 1; i >= 0; i--) {
if (indexKeys[i] < n - m + i) {
indexKeys[i]++;
for (int16_t j = i + 1; j < m; j++)
indexKeys[j] = indexKeys[j - 1] + 1;
return 1;
}
}
return 0;
}
void doCalculations(void) {
memset(sizes, 0, sizeof(sizes[0]) * (totalSpanners * 2)); // Обнуление массива размеров.
memset(cSizes, 0, sizeof(cSizes[0]) * (totalSpanners * 2)); // Обнуление массива кол-ва одинаковых размеров.
// Поиск более дорогих дублей и удаление их сдвигом из массива структур
totalSpanners1 = totalSpanners;
for (uint8_t i=0; i < totalSpanners1; i++)
for (uint8_t j=i+1; j < totalSpanners1; j++)
if ((((spanners[i].size1 == spanners[j].size1) && (spanners[i].size2 == spanners[j].size2)) ||
((spanners[i].size1 == spanners[j].size2) && (spanners[i].size2 == spanners[j].size1)))) {
if (spanners[i].price >= spanners[j].price) {
for (uint8_t k = i; k < totalSpanners1 - 1; k++)
spanners[k] = spanners[k + 1];
i--;
} else {
for (uint8_t k = j; k < totalSpanners1 - 1; k++)
spanners[k] = spanners[k + 1];
j--;
}
totalSpanners1--;
}
// Если все дубли выводим результат с одним ключём
if (totalSpanners1 == 1) {spanners[0].included = 1; return;}
// Поиск всех размеров ключей и вычисление кол-ва каждого размера
for (uint8_t i = 0; i < totalSpanners1; i++) {
for (uint8_t j=0; j < allSizes; j++) {
if (sizes[j] == spanners[i].size1) {flag1=1; cSizes[j]++;}
if (sizes[j] == spanners[i].size2) {flag2=1; cSizes[j]++;}
}
if (!flag1) {sizes[allSizes] = spanners[i].size1; allSizes++;} else flag1=0;
if (!flag2) {sizes[allSizes] = spanners[i].size2; allSizes++;} else flag2=0;
}
// Добавление ключей с одним размером в результирующий набор
for (uint8_t i = 0; i < allSizes; i++)
if (cSizes[i] == 0)
for (uint8_t j=0; j < totalSpanners1; j++) {
if (spanners[j].included) continue;
if (sizes[i] == spanners[j].size1 || sizes[i] == spanners[j].size2)
spanners[j].included = 1;
}
// Пометка вторых размеров из уже выбранных ключей на удаление
for (uint8_t i=0; i < totalSpanners1; i++)
if (spanners[i].included)
for (uint8_t j = 0; j < allSizes; j++)
if (sizes[j] == spanners[i].size1 || sizes[j] == spanners[i].size2) cSizes[j] = 0;
do {
flag2=0;
// Удаление размеров которые уже есть в выбраных ключах
for (uint8_t i = 0; i < allSizes; i++)
if (cSizes[i] == 0) {
for (uint8_t j = i; j < allSizes - 1; j++)
{sizes[j] = sizes[j+1]; cSizes[j] = cSizes[j+1];}
allSizes--; i--;
}
// Удаление ключей у которых оба размера совпадает с размерами уже выбранных ключей.
for (uint8_t i = 0; i < totalSpanners1; i++) {
if (spanners[i].included) continue;
flag1=0;
for (uint8_t j = 0; j < allSizes; j++)
if (sizes[j] == spanners[i].size1 || sizes[j] == spanners[i].size2) flag1=1;
if (!flag1) {
for (uint8_t k = i; k < totalSpanners1 - 1; k++)
spanners[k] = spanners[k + 1];
totalSpanners1--; i--;
spanners[totalSpanners1].included = 0;
}
}
// Удаляем более дорогие ключи с одним оставшимся размером, а если размера только два, второй ключ добовляем в результирующий набор, помечая размеры на удаление из этого ключа.
for (uint8_t i = 0; i < allSizes; i++)
for (uint8_t j = 0; j < totalSpanners1; j++) {
if (spanners[j].included) continue;
flag3=1;
if (sizes[i] == spanners[j].size1) {
for (uint8_t l = 0; l < allSizes; l++)
if (l != i)
if (sizes[l] == spanners[j].size2) {flag3=0; break;}
if (flag3) {
for (uint8_t k = 0; k < totalSpanners1; k++) {
if (spanners[k].included) continue;
if (k != j)
if (sizes[i] == spanners[k].size1 || sizes[i] == spanners[k].size2)
if (spanners[j].price >= spanners[k].price) {
if (cSizes[i] == 1) {
spanners[k].included = 1;
for (uint8_t o = 0; o < allSizes; o++)
if (sizes[o] == spanners[k].size1 || sizes[o] == spanners[k].size2) cSizes[o] = 0;
} else cSizes[i]--;
for (uint8_t o = j; o < totalSpanners1 - 1; o++)
spanners[o] = spanners[o + 1];
totalSpanners1--; j--; flag2=1;
spanners[totalSpanners1].included = 0;
break;
}
}
}
}
if (sizes[i] == spanners[j].size2) {
for (uint8_t l = 0; l < allSizes; l++)
if (l != i)
if (sizes[l] == spanners[j].size1) {flag3=0; break;}
if (flag3) {
for (uint8_t k = 0; k < totalSpanners1; k++) {
if (spanners[k].included) continue;
if (k != j)
if (sizes[i] == spanners[k].size1 || sizes[i] == spanners[k].size2)
if (spanners[j].price >= spanners[k].price) {
if (cSizes[i] == 1) {
spanners[k].included = 1;
for (uint8_t o = 0; o < allSizes; o++)
if (sizes[o] == spanners[k].size1 || sizes[o] == spanners[k].size2) cSizes[o] = 0;
} else cSizes[i]--;
for (uint8_t o = j; o < totalSpanners1 - 1; o++)
spanners[o] = spanners[o + 1];
totalSpanners1--; j--; flag2=1;
spanners[totalSpanners1].included = 0;
break;
}
}
}
}
}
} while (flag2);
// Убираем уже выбранные ключи в конец массива
for (uint8_t i = 0; i < totalSpanners1; i++)
for (uint8_t j = 0; j < totalSpanners1 - 1; j++)
if (spanners[j].included > spanners[j+1].included) {
tempS[0] = spanners[j];
spanners[j] = spanners[j + 1];
spanners[j + 1] = tempS[0];
}
// Считаем оставшиеся ключи.
for (uint8_t i = 0; i < totalSpanners1; i++)
if (spanners[i].included == 1) totalSpanners1 = i;
//Сортировка оставшихся ключей по цене от большей к меньшей
for (uint8_t i = 0; i < totalSpanners1; i++)
for (uint8_t j = 0; j < totalSpanners1 - 1; j++)
if (spanners[j].price < spanners[j+1].price) {
tempS[0] = spanners[j];
spanners[j] = spanners[j + 1];
spanners[j + 1] = tempS[0];
}
bool stopElem=0;
int16_t n=totalSpanners1;
uint8_t *indexKeys, rezIndexKeys[n], rezM=0, elem=0;
indexKeys = new int8_t[n];
uint64_t rezSumPrice = UINT64_MAX, sumPrice = 0;
// перебор сочетаний индексов массива ключей
// n - кол-во оставшихся не выбранных ключей
// m - от половины оставшихся размеров до кол-ва не выбранных ключей
for (int16_t m=round(((float)allSizes/2)); m <= n; m++) {
for (int16_t i = 0; i < n; i++)
indexKeys[i] = i;
do {
sumPrice = 0; flag1=1;
// Проверка, что сумма сочетания меньше суммы предыдущего выбранного сочетания
for (int16_t i = 0; i < m; i++) {
sumPrice = sumPrice + uint64_t(spanners[indexKeys[i]].price);
if (sumPrice >= rezSumPrice) {flag1 = 0; break;}
}
if (flag1) {
flag2=1;
// Проверка наличия всех оставшихся размеров в сочетании
for (uint8_t i=0; i < allSizes; i++) {
for (uint8_t j=0; j < m; j++) {
if ((spanners[indexKeys[j]].size1 == sizes[i]) || (spanners[indexKeys[j]].size2 == sizes[i])) break;
if (j == m-1)
if ((spanners[indexKeys[j]].size1 != sizes[i]) && (spanners[indexKeys[j]].size2 != sizes[i]))
flag2=0;
}
if (!flag2) break;
}
}
// Если обе проверки пройдены сохраняем результат
if (flag1 && flag2) {
rezSumPrice = sumPrice;
rezM = m;
for (uint8_t j = 0; j < m; j++)
rezIndexKeys[j] = indexKeys[j];
}
} while (nextSet(indexKeys, n, m));
}
// Добавляем ключи выбранные на основе перебора в результирующий набор
for (uint8_t i = 0; i < rezM; i++) {
spanners[rezIndexKeys[i]].included = 1;
}
}
void printResults(void) {
Serial.println(F("Ключи"));
uint32_t total = 0;
for (uint8_t i = 0; i < totalSpanners; i++) {
if (spanners[i].included) {
Serial.print(i + 1);
Serial.print(". ");
Serial.println(spanners[i]);
total += spanners[i].price;
}
}
Serial.print(F("Стоимость набора: "));
Spanner::printPrice(Serial, total);
Serial.println();
}
void setup(void) {
Serial.begin(9600);
const uint32_t start = millis();
if (totalSpanners > 100) {Serial.println(F("Колличество ключей больше 100. Уменьшите их колличество.")); return;}
doCalculations();
const uint32_t duration = millis() - start;
printResults();
Serial.print(F("Время расчёта: "));
Serial.print(duration);
Serial.println(F(" миллисекунд"));
}
void loop(void) {}
Но результат с первым не сходиться. Где-то ошибка.
Какой результат не сходится? Пишите два варианта ответа - я вам с одного взгляда :) скажу. где правильный - я уже столько раз прогонял все тесты на куче вариантов своего кода, что выучил все результаты наизусть :)
Андрей, Ваша оптимизация, изложенная в #275 и так работает достаточно эффективно. До пункта 5 сам не додумался, поэтому применять у себя не стал - ибо неспортивно:) Во всяком случае мое решение Вы переигрываете вчистую на большинстве тестовых наборов.
Андрей, Ваша оптимизация, изложенная в #275 и так работает достаточно эффективно. До пункта 5 сам не додумался, поэтому применять у себя не стал - ибо неспортивно:) Во всяком случае мое решение Вы переигрываете вчистую на большинстве тестовых наборов.
Тут ещё всё зависеть от наборов ЕвгенияП, правильности результатов всех этих наборов, отсутствие зацикливаний и что у нас по среднему получиться.
Андрей, Ваша оптимизация, изложенная в #275 и так работает достаточно эффективно. До пункта 5 сам не додумался, поэтому применять у себя не стал - ибо неспортивно:)
п 5 очень эффективный, да :) Особенно для "линейных" наборов ключей. В исходном наборе Евгения п5 едва не позволяет найти оптимум чисто аналитическим путем, вообще без переборов :)
Я специально в #268 предложил "закольцевать" граф ключей, чтобы поиск минимума не был таким простым.
Так какова же была цель такого искажения?
"когда в обществе нет цветовой дифференциации штанов то нет и цели"
цифры реальные с монитора порта...почему у коляна несколько не те - не знаю...ядро минисоре оптимизация включена
Со временем выполнения тут есть некая странность. Я уже описывал в #154. Кроме того, проверял код АндреяД, в 1.8.7 исполняется дольше (мой, наоборот, быстрее). А в 1.8.13 - код АндреяД у меня и у него выполняются за одинаковое время.
Да, у меня получился алгоритм для определённых типов стартовых наборов, сделать его универсальным у меня не получилось.
Результат:
Андрей! это ПАТАМУШТА задачу решить пытаешься чисто математически, а она всё таки прикладная, размеры ключей в паре формируются не с потолка, а из ограничений на усилия, то-есть с ключом на 10 не может быть в паре размерность к примеру 17 (и выше) подумай, как это можно обыграть, да никто не мешает тебе сделать массив размерностей (5,6,7...) и его использовать, они стандартизированы и, смотришь, в один проход с дельтой возврата получится реализовать
В задании не сказано, что ключ это именно гаечный ключ. Может это ключ от входной двери с двумя размерами бородок сваренных между собой, ну как вариант. )
Т.е. на входе имеем совокупность объектов под названием "ключ" с тремя свойствами: 1. первый ненулевой размер; 2. второй ненулевой размер не равный первому. 3. цена.
Можно даже подтянуть за уши, что цена это не денежный эквивалент, а, например, затраты времени на использование ключа. И тогда может это составные ключи базы данных, состоящий из двух размеров (длин).
если абстрактная, тогда да
Хех, типа, а вдруг новичок докажет P=NP, просто потому, что не знает об этом?
Поучаствую-ка и я, моё решение вне конкурса и не для ардуины, зато самое быстрое по времени создания - менее 5 минут, решает за 500 мксек, ну правда и цпу пошустрее, чем атмега.
Таким образом, получаем минимальное остовное дерево, которое нам и требовалось. Понравился прикол с размерами ключей 12-13-15 и 12-14-15. В решении 42 строки, что доказывает, что смысл жизни - в знаниях :)
PS Кто не писал алгоритм для упаковки товаров со свойствами Ш*Д*В в коробки доставки Ш*Д*В - жизни не видел. :)
Таким образом, получаем минимальное остовное дерево, которое нам и требовалось.
А Вы думаете,что эта простая и очевидная на первый взгляд, однако неправильная в корне идея осенила только Вас;) Ошибаетесь. Кста и на Ардуине она решается за сопоставимое время, поверьте мне - она у мной написана. Только зто задача не про связный граф, а про множество() графов из двух узлов и одной дуги. И мин. деревом для ЛЮБОГО ключа будет его ЦЕНА. И в верное решение могут входить ключи, которые не входят в остовное, которое Вы изобразили...
kolyn
Да, верно, нужен другой лес, не такой.
Андрей! это ПАТАМУШТА задачу решить пытаешься чисто математически, а она всё таки прикладная, размеры ключей в паре формируются не с потолка, а из ограничений на усилия
а ты все время в чисто программисткую задачу пытаешься внести сермяжный смысл , достойный кладовщика инструментального цеха.
Слушай, мне как програмисту глубоко плевать на размеры по ГОСТу. Программа должна работать с любыми наборами, даже совершенно бредовыми, типа 3х55, иначе это не программа.
И от меня просьба - если тебе нечего сказать по сути конкурса - лучше не говори ничего
Да, у меня получился алгоритм для определённых типов стартовых наборов, сделать его универсальным у меня не получилось.
Андрей, посмотрел код... никак не могу понять, почему вы сделали ключевой характеристикой поиска не цену, а разницу между размерами на концах ключа. Какое отношение это число имеет к оптимальному набору? С чего вы взяли, что ключ 14Х17 обязательно выгоднее 17х19 ?
То, что это (совершенно случайно) оказалось верным для тестового набора - никак не помогает в работе с другими наборами. Поэтому, как мне кажется, ваш алгоритм и не работает.
Думаю, это влияние ua6em... это он задурил вам готову своими ГОСТами и словами, что размеры ключей должны быть строго определенных комбинаций и заданы в соответвии с реальной линейкой размеров... Это неверно. На самом деле по условию размеры могут быть абсолютно любыми..
У меня получился алгоритм для конкретного набора. Разрабатывал его от правильного результата. Т.е. посмотрел какие должны быть выбраны ключи и как их надо выбирать и нашёл такую закономерность.
Уже делаю другой алгоритм.
Вроде что-то годное у меня получается, только что выразил в коде свой новый алгоритм, но нужно еще проверять. Результаты на данный момент.
Набор из задания:
Набор от kolyn:
Результат:
Запустил ГОСТовский набор полностью:
При компиляции последнего:
Блин, мужики, Вы меня ну очень приятно удивляете! Спасибо!
Интересно, сколько в итоге будет участников, и сколько будет 100% рабочих вариантов.
Жаль ничего не петрю в алгоритмах и математике, тоже бы поучаствовал. Помню, лет 20 назад участвовал в конкурсе программирования игр, Lines была задача написать. Сколько то шаров одноцветных, несколько двуцветных и один универсальный. Интересно было написать алгоритм проверки на 5 одинаковых цветов в линии. Даже выиграл тогда, но уровень соперников не известен, и никто не написал 100% рабочего варианта, а так хотелось сравнить.
А чем мой предпоследний вариант не рабочий? Он только долгий. Да и у kolyn, вроде рабочие варианты.
Свой новый алгоритм хочу ещё потестить, "поиграться" с оптимизацией по времени. До конца конкурса ещё шесть дней, даже если и найду баги, всё равно выложу свой последний код на выходных.
вроде рабочие варианты
Я не говорю, что не рабочие. Дождёмся экспертного мнения Евгения Петровича.
Про наборы
Кроме числа ключей имеет смысл еще поиграться связностью. Например наборы от ua6em - хоть и хороши для "нагрузочного тестирования" - в основном сложны обьемом, а топография у них довольно простенькая, почти линейная. О чем я веду речь?
Вот, к примеру, набор Евгения Петровича:
Видно. что в нем есть ветвления и циклы. Но так же есть и длинные "хвосты", которые сильно облегчают решение.
Добавим к нему всего один ключ - и набор станет совсем другим:
И вот самое интересное - насколько будет отличаться время решения этих двух наборов алгоритмами разных авторов :)
Чисто практически - набор ЕП у меня считается за 2.8 мс, а если добавить к нему ключик со следующими парметрами:
то время расчета возрастает сразу до 14.2 мс - ровно в пять раз.
Всего один ключик. а такая разница :)
PS Кто не писал алгоритм для упаковки товаров со свойствами Ш*Д*В в коробки доставки Ш*Д*В - жизни не видел. :)
что считать 100% рабочим вариантом? Зависит от того, как тестировать. Если отбросить явные ошибки алгоритмов, то все упирается в память. Памяти в атмеги328 мало. а очевидно что чем больше ключей - тем быстрее она кончается.
На данный момент у меня есть код, который все небольшие наборы из ветки решает влет :) Но наборах от товарища ua6em он затыкается. Вот уже неделю бьюсь, выкинул из кода все лишнее, выиграл в памяти почти в 2 раза - но до успеха еще очень далеко. Например, набор из сообщения #220 требует для решения 12 уровней рекурсии. А память заканчивается на четвертом :( И хотя каждый следующий уровень меньше предыдущего - все-таки разница между 4 и 12-тью - не очень обнадеживающая :)))
С массивом из 100 ключей пустой скелет выдаёт: Глобальные переменные используют 1210 байт (59%).
Как вариант можно переместить included в несколько uint64_t, а оставшийся массив структур в PROGMEM, если, конечно, никаких манипуляций по изменению массива структур код делать не будет.
По моему коду.
Полный ГОСТовский набор у меня за сутки так и не посчитался, если его разделить примерно пополам, обе половины проходят.
Результаты до 30 ключей:
Набор из 100 ключей, где 75 дубли по размерам:
Компилятор:
Результат:
Результат набора с добавлением
{9, 27, 71570} в набор из задания:
Набор из 100 ключей, где 75 дубли по размерам или уникальные ключи (т.е. оба размера ключа больше не присутствуют):
Результат:
Буду благодарен, если предложите наборы для тестирования, на условиях:
// Ключей в массиве не больше 100
// Размеры в одном ключе целочисленные, не равны 0, не равны между собой, и каждый не больше 255
// При наличии пресекающихся по размерам ключей в кол-ве больше 30 вычисления займут длительное время
Нашел в коде серьезный косяк. Исправленная версия:
Результат выполнения в 1.8.13
К сожалению в плане оптимизации по времени ничего не выжал. При переборе с пом. "сочетаний без повторений" метод ветвей - границ в полной мере применить не сумел.
Тоже использую сочетания без повторений, но немного по другому. Подсказка. :) Время исполнения моего алгоритма примерно половина от вашего времени. Хотя, может о чём я думаю уже у вас реализовано, не вникал в ваш код.
И ещё появилась идея как ускорить, думаю, за выходные успею до ума довести.
Выложу свой код, а то сроки подходят к концу
Это вариант "А", так называемый быстрый, без экономии памяти. Для наборов более 20 ключей может вываливаться с сообщениям о недостатке памяти или тихо виснуть (долго ждать не надо, если на экране ничего нет более секунды - значит завис :)
Зато мелкие наборы считает быстро
Код:
Результаты для кода выше (Ардуино ИДЕ 1.6.12):
Тестовый набор ЕП - 2.68 мс
Нфбор от Kolyn - 3.92 мс
Результаты для последних строчек урезанного набора ГОСТ ниже
Как видно, скорость работы зависит от числа ключей совсем не однозначно... Строчки выше тестировать не стал
Наборы из 100 ключей не работают.
Проверяйте
Есть еще вариант"Б" - медленный , зато более экономный до ресурсов. Его, если будет интерес, выложу позже. Но большой разницы там нет, например по ГОСТовскому набору максимум для варианта А - 30 ключей, для Б - 34, зато скорость ниже примерно в 5 раз
Моё виденье каким должен быть алгоритм, а вдруг ещё есть молчуны-участники и смогут осуществить за три дня:
Возможно, в процессе фильтрации можно разделить набор на поднаборы и уже к ним применять пункт 2.
Или при грамотно продуманной фильтрации перебор и не понадобиться.
Фильтрация:
Виденье - это хорошо, а что сами не реализуете?
без 6 пункта уже реализовал, попробую за оставшееся время конкурса успеть.
Ну и 7 конечно.
без 6 пункта уже реализовал, попробую за оставшееся время конкурса успеть.
касаемо вашей идеи насчет п 6, то далеко не в каждом наборе многократный проход дает выигрыш.
Я в сообщении 268 рисовал графы, в исходном наборе ваш п6 выгоден, а "кольцевом" мало что даст
Выложу свой код, а то сроки подходят к концу
Хотелось бы еще минимальных авторских комментариев к коду, желательно на "могучем". Патамушта не все могут уловить главную сюжетную линию;) Я - так точно;)))
Если есть конкретные вопросы, могу ответить. Правда, подробные комменты - только в понедельник, а то я с телефона
Смог сформулировать лишь общий - "как это работает?". Поэтому подожду.
Примерно, как описано у Андрея в 275, только в конце не перебор, а рекурсия на один шаг глубже и снова эти же 7 пунктов... И так пока граф ключей не кончится
Сегодняшние результаты:
Набор из задания - 18 миллисекунд
С добавлением к нему {9, 27, 71570} - 1902 миллисекунд
Набор от kolyn: 8 миллисекунд
До 30 ГОСТовских ключей -
Ого, борьба обостряется :)
Может еще участники подтянутся? Еще есть порядка полутора суток :) - можно многое успеть.
Кстати, Евгений, по условиям конкурс закрывается в 0:00 час 1 декабря - по какому часовому поясу ? :)
Вроде правильно осуществил свой словесный алгоритм. Завтра (30 ноября) ещё потестирую с другими наборами и выложу код.
Набор из задания - 4 миллисекунд
С добавлением к нему {9, 27, 71570} - 1921 миллисекунд
Набор от kolyn: 8 миллисекунд
До 30 ГОСТовских ключей -
Набор из задания - 4 миллисекунд
С добавлением к нему {9, 27, 71570} - 1921 миллисекунд
Ого, как "несимметрично" у вас... один ключ добавили и 500 раз разницы...
Ого, как "несимметрично" у вас... один ключ добавили и 500 раз разницы...
15 ключей и 12 размеров "улетели" в перебор.
Выкладываю окончательную версию. Завтра и компьютер включать не буду:)
Набор из задания Время расчёта: 198 миллисекунд
Все тесты, которые я придумал или те корректные, что уже были ветке, у меня проходят
Строки 152-204 написал только сегодня, получилось коряво, ну раз работает не стал трогать.
Мой код:
Все тесты, которые я придумал или те корректные, что уже были ветке, у меня проходят
потестировал, все отлично, результаты совпадают.
А можно два варианта на конкурс выложить? Второй не до конца проверенный.
Но результат с первым не сходиться. Где-то ошибка.
Но результат с первым не сходиться. Где-то ошибка.
Какой результат не сходится? Пишите два варианта ответа - я вам с одного взгляда :) скажу. где правильный - я уже столько раз прогонял все тесты на куче вариантов своего кода, что выучил все результаты наизусть :)
Немного подправил, но всё равно не то.
У меня на третий день программирования уже мозги плавятся. Пусть предыдущий код будет на конкурс, а этот на дальнейшее обсуждение.
Сравнение результатов с набором из задания:
Ну тут вроде очевидно все - во-втором наборе и ключей на один больше, и цена почти на 10%...
Не вижу смысла выкладывать вариант, который ошибается хотя бы на одном тесте.
Андрей, Ваша оптимизация, изложенная в #275 и так работает достаточно эффективно. До пункта 5 сам не додумался, поэтому применять у себя не стал - ибо неспортивно:) Во всяком случае мое решение Вы переигрываете вчистую на большинстве тестовых наборов.
Ну тут вроде очевидно все - во-втором наборе и ключей на один больше, и цена почти на 10%...
Не вижу смысла выкладывать вариант, который ошибается хотя бы на одном тесте.
Завтра опишу словестно , что хоте сделать. Скорее всего, где-то закрался баг.
Андрей, Ваша оптимизация, изложенная в #275 и так работает достаточно эффективно. До пункта 5 сам не додумался, поэтому применять у себя не стал - ибо неспортивно:) Во всяком случае мое решение Вы переигрываете вчистую на большинстве тестовых наборов.
Тут ещё всё зависеть от наборов ЕвгенияП, правильности результатов всех этих наборов, отсутствие зацикливаний и что у нас по среднему получиться.
Андрей, Ваша оптимизация, изложенная в #275 и так работает достаточно эффективно. До пункта 5 сам не додумался, поэтому применять у себя не стал - ибо неспортивно:)
п 5 очень эффективный, да :) Особенно для "линейных" наборов ключей. В исходном наборе Евгения п5 едва не позволяет найти оптимум чисто аналитическим путем, вообще без переборов :)
Я специально в #268 предложил "закольцевать" граф ключей, чтобы поиск минимума не был таким простым.
А можно два варианта на конкурс выложить?
Конечно
А можно два варианта на конкурс выложить?
Конечно
На конкурс у меня только один рабочий и более менее шустрый - в сообщении #339.