Історія
Моя компанія щотижнево розсилає інформаційний бюлетень усім в межах компанії. До цих бюлетенів входить загадка, а також вигук, хто в компанії першим надіслав електронною поштою / запропонував рішення загадки минулого тижня. Більшість цих загадок досить тривіальні і, чесно кажучи, досить тупі для технічної компанії, але був один, кілька місяців тому, що привернув мою увагу.
Оригінальна загадка:
З огляду на форму нижче:
У вас природні номери від 1 до 16. Встановіть їх у цю форму, щоб усі суміжні ряди та суміжні стовпці складали до 29.
Наприклад, одним із таких варіантів цієї головоломки (що було "канонічним" рішенням, яке я подав у розсилку) було таке:
Однак у процесі її вирішення я знайшов досить цікаву інформацію:
- Рішень є значно більше, ніж лише те; насправді існує 9 368 рішень.
- Якщо розгорнути набір правил, вимагаючи лише, щоб рядки та стовпці були рівні між собою, а не обов'язково 29, ви отримаєте 33 608 рішення:
- 4,440 Рішення на суму 27.
- 7400 Рішення на суму 28.
- 9,368 Рішення на суму 29.
- 696 Розв’язання на суму 30.
- 5,104 Рішення на суму 31.
- 1200 рішень на суму 32.
Тож я та мої колеги (хоча здебільшого просто мій менеджер, оскільки він був єдиною іншою людиною, ніж я, із навичками програмування "Загальна мета"), поставили перед собою виклик, який тривав більшу частину місяця - у нас була інша, фактична робота - пов'язані зобов’язання, які ми повинні були взяти на себе - спробувати написати програму, яка б найшвидше знаходила кожне рішення.
Оригінальна статистика
Перша програма, яку я написав для вирішення проблеми, просто перевіряла випадкові рішення знову і знову, і зупинялася, коли знайшла рішення. Якщо ви зробили математичний аналіз цієї проблеми, напевно, ви вже знаєте, що це не повинно було працювати; але мені якось пощастило, і програмі знадобилося лише хвилину, щоб знайти єдине рішення (те, що я розмістив вище). Повторні запуски програми часто займали цілих 10 або 20 хвилин, тому очевидно, це не було жорстким рішенням проблеми.
Я перейшов до рекурсивного рішення, яке повторювалось через кожну можливу перестановку головоломки, і відкинуло безліч розв’язків відразу, усунувши суми, які не були складені. Тобто, якщо перший рядок / стовпець, який я порівнював, вже не був рівним, я міг би негайно припинити перевірку цієї гілки, знаючи, що нічого іншого, що перебуває у головоломці, це не змінить.
Використовуючи цей алгоритм, я отримав перший «належний» успіх: програма могла генерувати і виплюнути всі 33 608 рішень за 5 хвилин.
Мій менеджер мав інший підхід: знаючи на основі моєї роботи, що єдино можливі рішення мають суми 27, 28, 29, 30, 31 або 32, він написав багатопотокове рішення, яке перевіряло можливі суми лише для тих конкретних значень. Йому вдалося запустити свою програму лише за 2 хвилини. Тож я повторив ще раз; Я хеширував усі можливі 3/4-значні суми (на початку програми; вона рахується в загальній тривалості виконання) і використовував "часткову суму" рядка для пошуку залишкового значення на основі раніше завершеного рядка, а не випробувавши всі залишилися значення і звів час до 72 секунд. Тоді, з деякою логікою багатопотокової передачі, я знизив її до 40 секунд. Мій менеджер взяв програму додому, провів оптимізацію роботи програми та знизив її до 12 секунд. Я упорядкував оцінку рядків і стовпців,
Найшвидший з нас отримав наші програми через місяць, для мого менеджера - 0,15 секунди, а для мене - 0,33 секунди. Я, нарешті, стверджував, що моя програма була швидшою, оскільки програма мого менеджера, поки вона знайшла всі рішення, не надрукувала їх у текстовий файл. Якщо він додав таку логіку до свого коду, це часто займало 0,4-0,5 секунди.
З тих пір ми дозволили втриматись всередині особистого виклику, але, звичайно, залишається питання: чи можна цю програму зробити швидше?
Це виклик, який я буду ставити перед вами, хлопці.
Ваш виклик
Параметри, за якими ми працювали, зменшили правило "сума 29", а натомість "всі рядки / стовпці" сум рівні ", і я збираюся встановити це правило і для вас, хлопці. Завдання, таким чином, є: Написати програму, яка знайде (і надрукує!) Всі рішення цієї загадки в найкоротші терміни. Я збираюся встановити межу для поданих рішень: Якщо програма займає більше ніж 10 секунд на порівняно пристойному комп'ютері (<8 років), це, мабуть, занадто повільно, щоб рахувати.
Також у мене є кілька бонусів за головоломку:
- Чи можете ви узагальнити рішення так, щоб воно працювало для будь-якого набору з 16 чисел, а не тільки
int[1,16]
? Оцінка часу визначається на основі оригінального набору номерів підказки, але передається через цей кодовий шлях. (-10%) - Чи можете ви написати код таким чином, щоб він граціозно обробляв і вирішував дублікати цифр? Це не так просто, як може здатися! Рішення, які "візуально ідентичні", повинні бути унікальними в наборі результатів. (-5%)
- Чи можете ви впоратися з негативними цифрами? (-5%)
Ви також можете спробувати створити рішення, яке обробляє номери з плаваючою точкою, але, звичайно, не шокуйтеся, якщо це не вдасться до кінця. Якщо ви все-таки знайдете надійне рішення, це, можливо, коштує великого бонусу!
За всіма намірами та цілями "Повороти" вважаються унікальними рішеннями. Тож рішення, яке є лише обертанням іншого рішення, вважається власним рішенням.
Ідентифікатор IDE, на якому я працюю на своєму комп’ютері, - це Java та C ++. Я можу приймати відповіді з інших мов, але вам може знадобитися надати посилання на те, де я можу отримати просте налаштування середовища виконання для вашого коду.