Показаны сообщения с ярлыком Project Euler. Показать все сообщения
Показаны сообщения с ярлыком Project Euler. Показать все сообщения

пятница, 30 мая 2008 г.

Project Euler, problem 191

Давно не заходил на Project Euler, успел опуститься до 3й сотни.
Проблема оказалась простой. Как обычно просмотр решений был гораздо интереснее.
Больше всего понравилось одно из решений на С++. Это было самое быстрое решение. Понравилось настолько что приведу его...

int euler_191()

{

    int O,Oa,Oaa,L,La,Laa,SO,SL,Result;

    O=1;

    Oa=1;

    L=1;

    SO=2;

    SL=1;

    for (i=2, i<=30,i++)

    {

       Oaa=Oa;

       Oa=O;

       O=SO;

       Laa=La;

       La=L;

       L=SL+SO;

       SO=O+Oa+Oaa;

       SL=L+La+Laa;

    }

    return SO+SL;

}



Думаю оно не очень понятно...поэтому обьяснение.
here are only 6 states in which strings can exist, divided into two groups. Three of those states are those where the strings don't contain any LATE occurence. The other three are those where the strings already contain one LATE occurence. Within each group, one state is when the string does not end with an absence, another when the string ends with only one absence, and the third when the string ends with two absences. Let's denote them as O, Oa, Oaa, L, La and Laa. Let's denote the sums of these two groups as SO and SL.

The initial states after the first day would be O=1, Oa=1 and L=1(all other states =0). The initial sums would be SO=2 and SL=1.
a) If absent on the following day, all strings in state Oa would become Oaa strings and all strings in state O would become Oa strings. Similarly, all strings in state La would become Laa strings and all strings in state L would become La strings.
b) If late on the following day, all strings in group O would become L strings.
c) If neither late nor absent on the following day, all strings in group O would become O strings (O=SO), and all strings in group L would become L strings in addition to (b) above such that L=SL+SO.

The following algo should produce the answer within 1 ms with most (if not all) programming languages. zeycus and logopetria have already posted similar algos in their own programming language.


Также понравилось рекурсивное решение.

int blah(int d, int a, bool l)

{

    return d == 0 ? 1 : (blah(d - 1, 0, l) + (a >= 2 ? 0 : blah(d - 1, a + 1, l)) + (l ? 0 : blah(d - 1, 0, true)));

}

воскресенье, 16 марта 2008 г.

Project Euler, problem 127

Чем мне нравится Project Euler - практически на любой задаче можно что-то для себя открыть/переоткрыть.

The radical of n, rad(n), is the product of distinct prime factors of n. For example, 504 = 23 × 32 × 7, so rad(504) = 2 × 3 × 7 = 42.
We shall define the triplet of positive integers (a, b, c) to be an abc-hit if:
1. GCD(a, b) = GCD(a, c) = GCD(b, c) = 1
2. a < b
3. a + b = c
4. rad(abc) < c
For example, (5, 27, 32) is an abc-hit, because:
1. GCD(5, 27) = GCD(5, 32) = GCD(27, 32) = 1
2. 5 < 27
3. 5 + 27 = 32
4. rad(4320) = 30 < 32
It turns out that abc-hits are quite rare and there are only thirty-one abc-hits for c < 1000, with ∑c = 12523.
Find ∑c for c < 110000.


После некоторого достаточно бесплодного с точки зрения результата размышления(о котором тоже в принципе интересно было бы написать) я пришел к решению проблемы практически "в лоб" - перебираем все числа a b c такие что a<b,a+b=c и с<110000
и проверяем выполняются ли для них условия 1. и 4.
Код:

    int count = 0;

    ulong64  sum = 0;

    for(int a=1;a<limit ;++a){

        int b = a+1;//a < b

        int c = a+b;//c = a + b

        while(c<limit){

            if( ! (cross(dpf[a],dpf[b]) || cross(dpf[a],dpf[c]) || cross(dpf[b],dpf[c]))){//GCD(a, b) = GCD(a, c) = GCD(b, c) = 1

                if(rad[a]*rad[b]*rad[c]<c_long){//rad(abc) < c

                    ++count;

                    sum+=c;

                }

            }

            c = a+(++b);

        }

    }




Здесь dpf[i] - массив из простых делителей числа i (например для 504 это будет [2,3,7])
rad[i] - произведение все чисел из массива dpf[i].
Функция cross(a,b) возвращает false если массивы a и b не пересекаются, т.е. нет такого числа i входящего одновременно в оба массива.
соответственно
if( ! (cross(dpf[a],dpf[b]) || cross(dpf[a],dpf[c]) || cross(dpf[b],dpf[c]))){
соответствует условию
1. GCD(a, b) = GCD(a, c) = GCD(b, c) = 1
Далее из этого же условия следует что rad(a*b*c) == rad(a)*rad)b)*rad(c), соответственно
if(rad[a]*rad[b]*rad[c]<c_long)
соответствует условию
4.rad(abc) < c

Вообщем надеюсь логика более менее понятна. На тестовом условии (для c<1000 count == 31 и sum == 12523) этот подход отрабатывал. Оставалась мелочь - запустить его же для c<110000.

Первая проблема была - ну оооочень долго выполняется. Когда спустя минут 10 я добрался до a=4000 я прервал выполнение.

Первая попытка оптимизации - если а и b четные их можно не проверять. Поэтому добавилось условие
if(a%2 == 1 || b%2 == 1)

Все равно очень долго...да и явно неправильный результат.
В чем проблема? переполнение... переполнение может происходить при проверке условия rad(a)*rad)b)*rad(c). Переходим к unsigned long long. Результат вроде становится правильным...но все равно ооочень долго.

Хм...хм...
Возможно кому-то решение видно сразу. Я думал достаточно долго. Даже было желание вновь придумать другой подход.
В результате прогнав несколько тестов (на меньшем значении c) я понял в чем дело.
Во внутреннем цикле используются два условия - 1. и 4. Результат должен удовлетворять обеим условиям. С этой точки зрения условия равнозначны. Но - они не равнозначны с точки зрения времени выполнения и частоты срабатывания.
Условие 4. срабатывает реже чем условие 1. И в то же время выполняется намного быстрее.

Поэтому все что нужно - поменять условия 1. и 4. местами...
Результат (я убрал переход к unsigned long long для наглядности):

    int count = 0;

    ulong64  sum = 0;

    for(int a=1;a<limit ;++a){

        int b = a+1;//a < b

        int c = a+b;//c = a + b

        while(c<limit){

            if(a%2 == 1 ||  b%2 == 1){

                if(rad[a]*rad[b]*rad[c]<c_long){//rad(abc) < c

                    if( ! (cross(dpf[a],dpf[b]) || cross(dpf[a],dpf[c]) || cross(dpf[b],dpf[c]))){//GCD(a, b) = GCD(a, c) = GCD(b, c) = 1

                        ++count;

                        sum+=c;

                    }

                }

            }

            c = a+(++b);

        }

    }


Время выполнения стало 29 секунд.

Выводы.
Порядок выполнения проверок имеет значение... Вывод в общем то очевидный. Но про него часто забывают.

Кстати, родилась простая задачка которую можно использовать на собеседованиях.
Есть два отсортированных по возрастанию массива целых чисел. Определить пересекаются ли эти массивы за время O(n+m), где n и m - длина массивов.

среда, 12 марта 2008 г.

Project Euler, problem 107

Project Euler, problem 107 - переизобретая алгоритмы на графах...
Интересная задача, особенно для тех кто не знает изначальный алгоритм. Собственно для таковых состоять она будет в том чтобы (пере)изобрести нужный алгоритм.

Мой первый вариант был очень похож на Prim's algorithm, но я просто не поверил(и не смог доказать) что он ведет к верному результату.
Финальный вариант больше подходил на алгоритм Дейкстры.

А в целом данная задача называется задачей нахождения Minimum spanning tree и решается с помощью алгоритмов Prim's algorithm и Kruskal's algorithm. Первоначальным же алгоритмом для этого класса задач является Borůvka's algorithm. Что характерно - все три алгоритма принадлежат к классу "жадных" алгоритмов.

понедельник, 3 марта 2008 г.

Project Euler, problem 100

Расту. Быстро набросав brute force переклчился на "листик и ручку". Оказалось - и тут Diophantine Equation

Потом посмотрел что там у меня набрутфорсилось...оказалось таки да, Diophantine Equation.

Дальше все было уже просто.

On-Line Encyclopedia of Integer Sequences - вообще полезный ресурс для этих задач

пятница, 22 февраля 2008 г.

Project Euler, problem 78

4 дня...4 дня...4 дня я изобретал формулу Эйлера, потом переоткрывал пентагональные числа...потом оптимизировал все это...

среда, 20 февраля 2008 г.

Project Euler, problem 117

What I really like about "Project Euler" competition is - sometimes problems that looks very hard have very easy solution. Actually they may be solved using "p&p" (paper and pencil)programing language.

Problem 117 looks beautiful. Its very easy to make complex recursive solution and a bit harder to think and make very simple solution without any recursion.

Here is mine:

LONGLONG euler_117()

{

    map<int,LONGLONG> _set;

    _set[0]=1;_set[1]=1;_set[2]=2;_set[3]=4;

    for(int i=4;i<=50;++i)

        _set[i]=_set[i-1]+_set[i-2]+_set[i-3]+_set[i-4];

 

    return _set[50];

}

суббота, 9 февраля 2008 г.

Project Euler Problem 145 - slap in the face...

Some positive integers n have the property that the sum [ n + reverse(n) ] consists entirely of odd (decimal) digits. For instance, 36 + 63 = 99 and 409 + 904 = 1313. We will call such numbers reversible; so 36, 63, 409, and 904 are reversible. Leading zeroes are not allowed in either n or reverse(n).

There are 120 reversible numbers below one-thousand.

How many reversible numbers are there below one-billion (10^9)?


Sounds simple? Sure...brute force it. Just like I do. Then go check the other solutions and feel yourself stupid bruteforcer...

This problem can be solved analytically.

пятница, 8 февраля 2008 г.

Find if digits in two numbers are permutations of each other

I was working on Project Euler problem 72

One subtask was to create function to check where numbers "a" and "b" are permutations of each other...
When I finish this problem and check other solutions on forum...I found few C++ solutions. The realization of this function was awful..and extremely slow.

Here is mine:

bool is_permutation(int a,int b)

{

    char test1[10]={0};

    char test2[10]={0};

    while(a>0 && b>0)

    {

        ++test1[a%10];

        ++test2[b%10];

        a/=10;b/=10;

    }

    if(a!=b)

        return false;

    return memcmp(test1,test2,10)==0?true:false;

}



There was one really close realization...
(you can ++test1[a%10];--test2[b%10]; and at the end check if test1=={0} )

But the others...

1st (O(n^2) on sorting numbers... cmon man can't you just qsort then):

#define CAP_LENGTH 8

inline int permutation(int a, int b){

    static int a_digits[CAP_LENGTH];

    static int b_digits[CAP_LENGTH];

    static int i,j, a_length, b_length;

    i=0;

    do{

        a_digits[i]=a%10;

        i++;

    } while( (a/=10)!=0 );

    a_length=i;

    i=0;

    do{

        b_digits[i]=b%10;

        i++;

    } while( (b/=10)!=0 );

    if(a_length!=i)

        return 0; //even digit number differ between a and b

    b_length=i;

    for(i=0; i<a_length; i++){

        // cycle on all a digits

        for (j=0; j<b_length; j++){

            // cycle on all the (remaining, valid) b digits

            if(a_digits[i]==b_digits[j]){

                b_digits[j]=-1;

                break;

            }

        }

        if(j==b_length)

            return 0;

    }

    return 1;

}



2nd (gogo STL...):

bool are_permutations (int num1, int num2)

{

    /* Must have the same number of digits. */

    if ((int) log10 (num1) != (int) log10 (num2))

        return false;

 

    std::deque<char> digits1, digits2;

    int sorted1 = 0, sorted2 = 0;

 

    while (num1 > 0)

    {

        digits1.push_back (num1 % 10);

        num1 *= 0.1;

    }

 

    while (num2 > 0)

    {

        digits2.push_back (num2 % 10);

        num2 *= 0.1;

    }

 

    std::sort (digits1.begin (), digits1.end ());

    std::sort (digits2.begin (), digits2.end ());

 

    for (std::deque<char>::iterator i = digits1.begin (); i != digits1.end ();

        ++i)

        sorted1 = sorted1 * 10 + *i;

 

    for (std::deque<char>::iterator i = digits2.begin (); i != digits2.end ();

        ++i)

        sorted2 = sorted2 * 10 + *i;

 

    return sorted1 == sorted2;

}



3rd (and the winner is...sprintf, qsort,strlen...):

bool perm(int a, int b)

{

    static char b1[1024];

    static char b2[1024];

 

    sprintf(b1, "%d", a);

    sprintf(b2, "%d", b);

 

    int l1 = strlen(b1);

    int l2 = strlen(b2);

 

    if (l1 != l2)

        return false;

 

    qsort(b1, l1, 1, cmp);

    qsort(b2, l2, 1, cmp);

 

    return !strcmp(b1, b2);

}