пятница, 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);

}

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

About one math puzzle...

If you feel your algorithm is too complex - you are right. Find another one, more simple.
If you feel your algorithm is too ugly - you are right. Find another one,more beautiful solution exists.

Project Euler, Problem 73

Consider the fraction, n/d, where n and d are positive integers. If n<d and HCF(n,d)=1, it is called a reduced proper fraction.

If we list the set of reduced proper fractions for d ≤ 8 in ascending order of size, we get:

1/8, 1/7, 1/6, 1/5, 1/4, 2/7, 1/3, 3/8, 2/5, 3/7, 1/2, 4/7, 3/5, 5/8, 2/3, 5/7, 3/4, 4/5, 5/6, 6/7, 7/8 (*)

It can be seen that there are 3 fractions between 1/3 and 1/2.

How many fractions lie between 1/3 and 1/2 in the sorted set of reduced proper fractions for d ≤ 10,000?


Why was this task interesting for me? Only because using mathematical knowledge I come to wrong solution...

So... Sequence (*) is so called Farey Sequence. One of the characteristics of this sequence:
let p/q, p'/q', and p''/q'' be three successive terms in a Farey series.
then p'/q'==(p+p'')/(q+q'')

It comes from that characteristics that if you have two terms in a Farey series you can produce p'/q', such as p/q<p'/q'<p''/q'' and
p'=p+p''
q'=q+q''
And then normalize p' and q' so HCF(p',q')=1

So we come to simple algorithm - take border elements (p/q=1/3,p''/q''=1/2), produce one more element from the sequence p'\q', then look how may more elements between (p/q,p'/q') and between (p'/q',p''/q'').
The only question - when we should stop? The first answer come to mind - when q'>limit.

So here is first solution:
LONGLONG fractions_between(int a,int c,int b,int d,int limit)
{
int _a1 = a+b;
int _c1 = c+d;
normalize(_a1,_c1);
LONGLONG result=0;
if(_c1>limit)
return 0;
return 1+fractions_between(a,c,_a1,_c1,limit)+fractions_between(_a1,_c1,b,d,limit);
}

LONGLONG euler_73()
{
return fractions_between(1,3,1,2,10000);
}


I run it for (1/3,1/2) and 8 as a limit and got 3, just as expected.
fractions_between(1,3,1,2,8)==3

So I run it for 10000 and got...stack overflow :).

Ok, some optimization to get rid of stack overflow...but when I check my result I got only "Sorry, but the answer you gave appears to be incorrect". I start to study the logic of my code and found that the logic was fine...

So if my answer is wrong the problem is in algorithm...but it so simple! I check again my assumption about algorithm - we should stop when q'>limit... Is it really true? Let's check sequence (1/8, 1/7, 1/6, 1/5) with limit 8. Elements 1/8 and 1/5 produce 2/13, so we should stop? No, because 1/8 and 2/13 produce 1/7, and 1/7 is tolerated fraction. So...when we should stop?

Ok, better assumption - we should stop when p/q and p''/q'' can't produce such p'/q' (and all other nested elements) such as q'limit...

Sounds logical...but... at this moment I have a filling that something is really wrong. The solution became too complex. It takes too long to produce the result. There must be a more simple solution.

And I usually trust myself...so I delete my solution and start to think about another one. Few minutes later it come to my mind:)

This leads me to common principle I write at the beginning.

суббота, 26 января 2008 г.

Шел бы ты дядя отсюда. А то ходят тут всякие...



А потом ложки пропадают.

Одно из возможных объяснений феномена исчезающих ложек — резистенциализм (resistentialism) — теория о том, что неодушевлённые предметы питают естественное отвращение к людям.


Думаю чаще изчезновение ложек можно обьяснить все таки естественным отвращением людей к неодушевленным ложкам.

- Матрица поимела тебя.
- Куда?
- Известно куда...

вторник, 22 января 2008 г.

Священные войны - где ставить фигурные скобки.

Одна из священных войн ведется вокруг расположения открывающей фигурной скобки.
Вариантов два - в конце текущей строки либо в начале следующей.
Т.е.
Вариант 1:
void foo()
{
bar();
if( condition() )
{
bar1();
bar2();
}
}

Вариант 2:
void foo() {
bar();
if( condition() ) {
bar1();
bar2();
}
}


Отвечаю на вопрос почему в этой войне сторонники скобки в конце предыдущей строки более агрессивны.

Приверженцев скобок на новой строке вариант со скоками в конце предыдущей строки немного раздражает. Привыкнуть можно...но неприятно. Как зуд.

В отличии от них приверженцев скобок на текущей сроке сами по себе скобки на новой строке бесят. Привыкнуть можно...но гораздо более сложно. Как к зубной боли.

Как правильно и почему реагируют именно так - вопрос открытый.

пятница, 18 января 2008 г.

Blogger + OpenID - продолжение

New feature: Blogger as OpenID provider.

To enable OpenID for your blogs, just edit your profile on draft.blogger.com and enable the checkbox which says Enable OpenID for Blogs and you are all set!

After checking this box, you can use the URL of any of the blogs you are an admin of as an OpenID identity. When you use it to log in to another site, you will be taken back to Blogger where you can confirm that Blogger can tell the site that you own the domain.

четверг, 17 января 2008 г.

Билл Гейтс - маленький домашний тиран...

Экс-президент Microsoft не пускает дочь за компьютер
Легендарный CEO Microsoft Билл Гейтс считает, что его дочь слишком проводит слишком много времени за компьютеров и супружеская пара Гейтсов решила ограничить доступ своего чада к компьютеризированным развлечениям.

Гейтс также заявил, что его 10-летняя дочь не была активным пользователем ПК до того, как она пошла в школу, где дети используют компьютеры практически для всего. Также дочь Билла Гейтса проводить огромное количество времени играя на Microsoft Xbox 360.

Теперь у дочери Гейтсов есть 45 минут в будние дни и 1 час в выходные для компьютерных развлечений, в то время как ограничение по времени для использования компьютера в учебных целях Гейтс вводить не стал.


Страсти какие...не то что у меня - 1 час в будние дни и 3 часа в выходные...

Blogger + OpenID

Подсмотрено туточки

Используем OpenID

Когда у меня уже был альманах на blogger.com появилось желание комментировать ЖЖ не как аноним, для этого пришлось зарегистрироваться ещё и там, а потом и на других ресурсах. Но тогда я не знал, что для этого можно использовать такую продвинутую фишку как OpenID. Эта технология позволяет зарегистрироваться только один раз на каком-либо сервере, а на все остальные ресурсы авторизоваться с помощью этого самого сервера. Большинство используют ЖЖ как OpenID сервер при авторизации на другие блоги, но мне хочется использовать ссылку на свой журнал в качестве OpenID. Вот как это делается.

Шаг первый – найти свободный OpenID сервер. Я использую pip.verisignlabs.com. Зарегистрироваться очень просто. Создать профайл тоже. Собственно после этого уже можно использовать выдаваемый ими URL а качестве OpenID – этот URL выгляди как логин.pip.verisignlabs.com – что не очень хорошо – в качетсве URL хочется использовать ссылку на свой журнал, на не pip.verisignlabs.com. Для этого надо добавить пару опций в настройки своего дневника на blogger.com.

Шаг второй – редактируем Template. В заголовке страницы (head) – вставить такие строчки (это образец! вместо слова логин указть ваш реальный логин):

<link rel="openid.server" href="http://pip.verisignlabs.com/server" />
<link rel="openid.delegate" href="http://логин.pip.verisignlabs.com/" />

После чего не забыть сделать republish и наслаждаться прозрачностью работы сетевых технологий! Тепрь можно отказаться от своего дневника на ЖЖ. Можно «дружить», комментировать как стандартный пользователь ЖЖ. Можно настроить свой профайл, загрузить картинки. А можно использовать свой OpenID и не только в ЖЖ. Успехов!

Ударим LINQом по Project Eiler

Задача 1:

If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.
Find the sum of all the multiples of 3 or 5 below 1000.


Решение.

static void eiler1(){
Console.WriteLine((from i in Enumerable.Range(0, 1000) where (i % 3 == 0) || (i % 5 == 0) select i).Sum());
}


такие дела.

Задача 2:
Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be:
1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...
Find the sum of all the even-valued terms in the sequence which do not exceed one million.


Решение (понадобилась дополнительная функция)

using System;
using System.Linq;
using System.Collections.Generic;

namespace eiler{
class Program
{
public static IEnumerable fibonator(int max){
int prev = 1;
int curr = 2;
while (true){
yield return curr;
int nprev = prev + curr;
curr += nprev;
prev = nprev;
if (curr >= max)
yield break;

}
}
static void Main(string[] args){
Console.WriteLine(
(from i in fibonator(1000000) select i).Sum() );
}
}
}

LINQанем анаграммы.

Задача (подсмотренно у Брэда Фитзпатрика)

Last night at Beau's party, one of Beau's guests mentioned he's expecting twins shortly, which is why is wife wasn't at the party.

I drunkenly suggested he name his kids two names that were anagrams of each other. I then wandered off downstairs to find such suitable names.



ЛИНКану решение:

   var pairs = from pair in
from name in
from name in File.ReadAllLines("d:\\dist.male.first") select name.Split(' ')[0]
group name by name.Sort()
where pair.Count() > 1
select pair;

foreach (var pair in pairs)
Console.WriteLine(String.Join(",", pair.ToArray()));


К сожалению в .NET Framework нет встроенной функции сортировки строки...
Поэтому полный вариант выглядит не так красиво. Опять же обрамление из using...

using System;
using System.IO;
using System.Linq;

namespace anagram
{
static class Program
{
public static string Sort(this string s)
{
char[] arr = s.ToArray();
Array.Sort(arr);
return new string(arr);
}

static void Main(string[] args)
{
var pairs = from pair in
from name in
from name in File.ReadAllLines("d:\\dist.male.first") select name.Split(' ')[0]
group name by name.Sort()
where pair.Count() > 1
select pair;

foreach (var pair in pairs)
Console.WriteLine(String.Join(",", pair.ToArray()));
}
}
}

вторник, 15 января 2008 г.

Интересная задача на переключение внимания.

Есть 6 шариков, 1 чёрный, 5 белых. Все шары одинаковые по весу, кроме одного из белых. За два взвешивания на рычажных весах найти этот бракованный шар.


Проблема здесь заключается в том что неизвестно тяжелее или легче "фальшивый" шар. А дающие решения как правило "по умолчанию" предполагают что "фальшивый" шар легче. Или тяжелее. Но обязательно что-то одно.