Моё решение задачи 60
+Краткое условие: необходимо найти множество из пяти простых чисел с минимальной суммой такое, что после “склеивания” в любом порядке любых двух чисел из него тоже будет простое число.
+diff --git a/archives.html b/archives.html index 7ac73ff..95075be 100644 --- a/archives.html +++ b/archives.html @@ -30,8 +30,10 @@
Краткое условие: необходимо найти множество из пяти простых чисел с минимальной суммой такое, что после “склеивания” в любом порядке любых двух чисел из него тоже будет простое число.
+На хабре когда-то увидел статью про то, что в Яндексе двум сотрудникам дали задачу на написание приложения, для вычисления выражений. Менеджер справился за 4 часа, а программист за два. Я решил попробовать свои силы.
diff --git a/authors.html b/authors.html index 801b288..4932eb6 100644 --- a/authors.html +++ b/authors.html @@ -29,7 +29,7 @@На хабре когда-то увидел статью про то, что в Яндексе двум сотрудникам дали задачу на написание приложения, для вычисления выражений. Менеджер справился за 4 часа, а программист за два. Я решил попробовать свои силы.
diff --git a/category/project-euler.html b/category/project-euler.html new file mode 100644 index 0000000..27ba514 --- /dev/null +++ b/category/project-euler.html @@ -0,0 +1,82 @@ + + + + + + + + +Project Euler и остальное
+ + +Краткое условие: необходимо найти множество из пяти простых чисел с минимальной суммой такое, что после “склеивания” в любом порядке любых двух чисел из него тоже будет простое число.
+Краткое условие: необходимо найти множество из пяти простых чисел с минимальной суммой такое, что после “склеивания” в любом порядке любых двух чисел из него тоже будет простое число.
+На хабре когда-то увидел статью про то, что в Яндексе двум сотрудникам дали задачу на написание приложения, для вычисления выражений. Менеджер справился за 4 часа, а программист за два. Я решил попробовать свои силы.
diff --git a/posts/eshche-odno-vychislenie-vyrazhenii/index.html b/posts/eshchio-odno-vychislenie-vyrazhenii/index.html similarity index 97% rename from posts/eshche-odno-vychislenie-vyrazhenii/index.html rename to posts/eshchio-odno-vychislenie-vyrazhenii/index.html index 6c565ef..463d528 100644 --- a/posts/eshche-odno-vychislenie-vyrazhenii/index.html +++ b/posts/eshchio-odno-vychislenie-vyrazhenii/index.html @@ -6,7 +6,7 @@ -Project Euler и остальное
+ + +Необходимо найти множество из пяти простых чисел с минимальной суммой такое, что после “склеивания” в любом порядке любых двух чисел из него тоже будет простое число. Здесь под процедурой “склеивания” чисел \(a\) и \(b\) подразумевается получения из \(a = \overline{a_1 a_2 \ldots a_n}\) и \(b = \overline{b_1 b_2 \ldots b_m}\) некоторого \(c\) так, что \(c = \overline{a_1 a_2 \ldots a_n b_1 b_2 \ldots b_m}\).
+Полное условие можно найти тут
+Для начала, можно понять, что непосредственный перебор “в лоб” слишком медленный и нужного результата не даст. Поэтому хочется уйти от, как мне кажется, не самого формализуемого условия к чему-то более простого, с чем проще работать. Давайте сначала поймём, какие вообще числа могут быть в одном множестве. Для этого достаточно перебрать все разбиения на два подчисла всех простых чисел. Это достаточно быстро, порядка \(O\left( N \right)\) операций. Важно не забыть, что мы можем разбивать число \(p\) только на \(\overline{p_1 p_2}\), между числами не может быть нулей! То есть если число 37 разбивается на 3 и 7, то 307 нет.
+Пусть мы получили набор таких разбиений, то есть набор пар вида \(\left( p, q \right)\). Давайте составим из них граф, где вершинки это простые числа, а ориентированное ребро из \(p\) в \(q\) означает, что есть пара \(\left( p, q \right)\). Из того, что порядок склеивания чисел произвольный сразу следует, что рассматриваемый граф должен быть неориентированным. Таким образом, все пары \(\left( p, q \right)\) для которых нет пары \(\left( q, p \right)\) необходимо выкинуть, а из оставшихся построить граф.
+Теперь задача стало гораздо понятнее: достаточно выбрать клику размера 5, что сумма значений её вершин минимальна. В общем случае, это достаточно ресурсоёмкая(как мне кажется) задача, но в реальном графе количество рёбер не слишком большое. В худшем случае, по теореме Турана, количество рёбер в графе лишь с одной такой кликой примерно на 10% меньше числа рёбер в полном графе.
+Непосредственно сам поиск такой клики можно реализовать тривиально. Ниже мой код на C++11 с использованием библиотеки Boost Graph Library (BGL).
+#include <iostream>
+#include <algorithm>
+#include <vector>
+#include <set>
+#include <map>
+#include <cstdint>
+
+#include <boost/graph/adjacency_list.hpp>
+#include "primesieve.hpp"
+
+using namespace std;
+using namespace boost;
+
+typedef adjacency_list<listS, vecS, undirectedS> Graph;
+
+template <class T>
+vector< T > getDigs(T num) {
+ vector< T > res;
+ while ( num != 0 ) {
+ res.push_back(num % 10);
+ num = num / 10;
+ }
+ reverse(res.begin(),res.end());
+ return res;
+}
+
+template <class T>
+T getNum(const vector<T> &digs){
+ T res(0);
+ T cur_mult(1);
+ for (auto it = digs.rbegin(); it != digs.rend(); ++it){
+ res += *it * cur_mult;
+ cur_mult *= T(10);
+ }
+ return res;
+}
+
+template <class T>
+vector< pair<T,T> > getPrimePairs(T prime, set<T> &primes) {
+ vector< pair<T,T> > res;
+ auto digs = getDigs<T>(prime);
+ for (size_t i = 1; i <= digs.size() - 1; ++i){
+ T q1 = getNum(vector<T>(digs.begin(), digs.begin() + i));
+ T q2 = getNum(vector<T>(digs.begin() + i, digs.end()));
+
+ if ( (primes.find(q1) != primes.end())
+ && (primes.find(q2) != primes.end()) && (digs.at(i) != 0) ){
+ res.push_back(make_pair(q1,q2));
+ }
+ }
+ return res;
+}
+
+// removes all (p,q) that (p,q) in edges but (q,p) is not
+template <class T>
+void delBadEdges(vector< pair<T,T> > &edges){
+ map< int64_t, int > mults; // contains counts of p * q
+
+ for (auto p: edges) {
+ int64_t key = p.first * p.second;
+ if ( mults.find(key) != mults.end() )
+ mults[key] += 1;
+ else
+ mults[key] = 1;
+ }
+ vector< pair<T,T> > res;
+ for (auto p: edges) {
+ int64_t key = p.first * p.second;
+ if ( mults[key] == 2 ){
+ res.push_back(p);
+ // edge (p,q) is already exists
+ mults[key] = 0;
+ }
+ }
+ swap(res, edges);
+}
+
+template <class T>
+set<T> getUniqNums(vector< pair<T,T> > &edges){
+ set<T> res;
+ for (auto p: edges) {
+ res.insert(p.first);
+ res.insert(p.second);
+ }
+ return res;
+}
+
+template <class T>
+void getNeedSet(Graph &g, Graph::vertex_descriptor st,int depth,
+ set<Graph::vertex_descriptor> cur_set, const map<Graph::vertex_descriptor, T> &vert_prime,
+ vector< set<Graph::vertex_descriptor> > &v_out){
+ auto edges = out_edges(st, g);
+ vector< Graph::vertex_descriptor > adj_verts;
+ for (auto e_it = edges.first; e_it != edges.second; ++e_it)
+ adj_verts.push_back(target(*e_it,g));
+
+ for (auto prev_v: cur_set)
+ if ( find(adj_verts.begin(), adj_verts.end(), prev_v) == adj_verts.end() )
+ return;
+
+ cur_set.insert(st);
+
+ if ( depth == 1 ){
+ v_out.push_back(cur_set);
+ return;
+ }
+
+ for (auto v: adj_verts){
+ if ( (vert_prime.at(v) < vert_prime.at(st)) )
+ continue;
+ getNeedSet(g,v,depth - 1, cur_set, vert_prime, v_out);
+ }
+}
+
+int main(int argc, char **argv) {
+ const int MAX_VAL = 100000000;
+ vector< long > v_primes;
+ primesieve::generate_primes(MAX_VAL, &v_primes);
+ set< long > s_primes(v_primes.begin(), v_primes.end());
+
+ cout << "Number of primes is " << v_primes.size() << endl;
+ v_primes.clear();
+
+ vector< pair< long,long > > all_edges;
+ for (auto pr: s_primes){
+ auto t_v = getPrimePairs(pr, s_primes);
+ copy(t_v.begin(),t_v.end(),back_inserter(all_edges));
+ }
+ cout << "There are " << all_edges.size() << " edges" << endl;
+ delBadEdges(all_edges);
+ cout << "After deleting bad edges: " << all_edges.size() << " edges" << endl;
+ // We only need this primes
+ s_primes = getUniqNums(all_edges);
+ map< Graph::vertex_descriptor, long > vert_prime;
+ map< long, Graph::vertex_descriptor > prime_vert;
+ Graph g;
+ for (auto pr: s_primes){
+ vert_prime[add_vertex(g)] = pr;
+ }
+ for (auto v: vert_prime){
+ prime_vert[v.second] = v.first;
+ }
+ for (auto p: all_edges)
+ add_edge(prime_vert.at(p.first),prime_vert.at(p.second),g);
+ auto all_vertices = vertices(g);
+ vector< set<Graph::vertex_descriptor> > goods;
+ for (auto v_it = all_vertices.first; v_it != all_vertices.second; ++v_it){
+ getNeedSet(g, *v_it,5, set<Graph::vertex_descriptor>(), vert_prime, goods);
+ }
+ vector< pair<size_t, size_t> > good_sums;
+ for (size_t i = 0; i < goods.size(); ++i){
+ cout << "Good set #" << i+1 << ": ";
+ size_t cur_sum = 0;
+ for (auto el: goods[i]){
+ el = vert_prime.at(el);
+ cur_sum += el;
+ cout << el << " ";
+ }
+ cout << endl;
+ good_sums.push_back(make_pair(cur_sum, i));
+ }
+ if ( !good_sums.empty() )
+ cout << "Result is " << min_element(good_sums.begin(),good_sums.end(), [](pair<size_t, size_t> a, pair<size_t, size_t> b){return a.first < b.first;})->first << endl;
+ return 0;
+}
+Ответ: 26033
+ +Project Euler и остальное
+ + +Краткое условие: необходимо найти множество из пяти простых чисел с минимальной суммой такое, что после “склеивания” в любом порядке любых двух чисел из него тоже будет простое число.
+Project Euler и остальное
+ + +Краткое условие: необходимо найти множество из пяти простых чисел с минимальной суммой такое, что после “склеивания” в любом порядке любых двух чисел из него тоже будет простое число.
+На хабре когда-то увидел статью про то, что в Яндексе двум сотрудникам дали задачу на написание приложения, для вычисления выражений. Менеджер справился за 4 часа, а программист за два. Я решил попробовать свои силы.
diff --git a/tag/project-euler.html b/tag/project-euler.html new file mode 100644 index 0000000..27ba514 --- /dev/null +++ b/tag/project-euler.html @@ -0,0 +1,82 @@ + + + + + + + + +Project Euler и остальное
+ + +Краткое условие: необходимо найти множество из пяти простых чисел с минимальной суммой такое, что после “склеивания” в любом порядке любых двух чисел из него тоже будет простое число.
+