Войти в систему

Home
    - Создать дневник
    - Написать в дневник
       - Подробный режим

LJ.Rossia.org
    - Новости сайта
    - Общие настройки
    - Sitemap
    - Оплата
    - ljr-fif

Редактировать...
    - Настройки
    - Список друзей
    - Дневник
    - Картинки
    - Пароль
    - Вид дневника

Сообщества

Настроить S2

Помощь
    - Забыли пароль?
    - FAQ
    - Тех. поддержка



Пишет Oleg Izhvanov ([info]izh) в [info]programming
@ 2007-06-24 19:27:00


Previous Entry  Add to memories!  Tell a Friend!  Next Entry
Простая задачка (с красивым решением)
Имеется последовательность из N объектов,
про любые два из которых можно сказать только
равны они или нет. Известно, что некоторого
объекта в последовательности больше половины.
Придумать алгоритм поиска этого объекта,
который бы завершался за один проход по
последовательности и использовал фиксированный
(то есть не зависящий от N) объем памяти.

Комменты скринятся.

Пояснение:

Понятно, что количество бит, при помощи которых
можно закодировать число N зависит от N, но
конкретно этой зависимостью можно пренебречь.
Также понятно, что от размера объекта зависит
объем используемой памяти. Этой зависимостью можно
также пренебречь. Существенно, чтобы количество
используемой памяти не зависело от количества
элементов в последовательности.

Решение (выделить мышкой):

Одно из возможных решений основывается на том
интересном факте, что из последовательности
можно выкинуть 3 произвольных попарно неравных
элемента. При этом количество искомых элементов
по прежнему будет больше половины в уменьшенной
последовательности. Другое решение (более простое),
предложенное Димой Калединым, также использует 
индукцию. Понять, как именно, предоставляется 
читателю в качестве упражнения.

Для простоты в решении рассматривается массив из
целых чисел. Алгоритмы, впрочем, будут работать для
любых объектов.

Мой вариант решения:

int find_most_repeatable(int *arr, size_t size)
{
    unsigned int        i;
    int                 a, b = 0, e, answer_a, answer_b, answer;

    answer_a = arr[0];
    a = 1;
    
    for (i = 1; i < size; i ++)
    {
        e = arr[i];
    
        if (answer_a == e && a > 0)
            a ++;
        else if (answer_b == e && b > 0)
            b ++;
        else
        {
            if (a == 0)
            {
                answer_a = e;
                a = 1;
                continue;
            }
            else if (b == 0)
            {
                answer_b = e;
                b = 1;
                continue;
            }

            a --; b --;
        }
    }

    if (a >= b)
        answer = answer_a;
    else
        answer = answer_b;
    
    return answer;
}

Вариант Димы Каледина (исправленный):

int find_most_repeatable2(int *arr, size_t size)
{
    unsigned int        i;
    int                 answer, e, c;
    
    answer = arr[0];
    c = 0; 
    for (i = 1; i < size; i ++)
    {
        e = arr[i];

        if (answer == e)
        {
            c ++;
            if (c <= 0)
                c ++;
        }
        else if (c >= 0)
            c --;

        if (c < 0)
            answer = e;
    }

    return answer;
}



(Добавить комментарий)


[info]666
2007-06-25 07:20 (ссылка)
мне кажется, решения у такой задачи нет

(Ответить) (Ветвь дискуссии)


[info]izh
2007-06-25 10:58 (ссылка)
У меня нет цели издеваться над членами
коммьюнити. Решение у этой задачи есть
и весьма изящное. Впрочем, если у Вас
доказательство несуществования, то буду
рад его слышать.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]666
2007-06-25 11:09 (ссылка)
ну, доказательства нет - чисто интуитивно мне так показалось.
с удовольствием посмотрел бы на решение.

(Ответить) (Уровень выше)


[info]666
2007-06-25 11:22 (ссылка)
кроме того, не ясно что значит "один проход по последовательности".
значит ли это что к каждому элементу последовательности
можно обращаться только 1 раз?
значит ли это что обращения должны быть последовательными?

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]izh
2007-06-25 11:29 (ссылка)
>можно обращаться только 1 раз?

Да. Представьте себе, что надо написать функцию с одним
параметром (элементом последовательности), которая
реализует требуемый алгоритм. Вам гарантируется, что
Вашу функцию позовут по разу для каждого элемента
последовательности.

>значит ли это что обращения должны быть последовательными?
Поскольку Вам неизвестно строение последовательности, то
это совершенно неважно. Естественно, что предполагать
какой-то определенный порядок взаимного расположения элементов
в последовательности нельзя.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]666
2007-06-25 12:17 (ссылка)
но если к кажому из элементов массива
можно обращаться только 1 раз,
то это противоречит условию:
"про любые два из которых можно сказать только
равны они или нет"

так как сравнивая 1й и 2й элементы,
я уже обращаюсь к каждому из них по 1-му разу,
и таким образом уже не могу сравнить ни один из них с 3-м.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]izh
2007-06-25 12:25 (ссылка)
>но если к кажому из элементов массива
>можно обращаться только 1 раз,
>то это противоречит условию:
>"про любые два из которых можно сказать только
>равны они или нет"

Разумеется нет. Например, Вы можете запомнить
(скопировать) один из элементов и сравнить его
с любым другим. Грубо говоря, Вам дана функция
(предикат), которая на входе имеет два объекта,
а на выходе булево значение "равны/неравны".

>так как сравнивая 1й и 2й элементы,
>я уже обращаюсь к каждому из них по 1-му разу,
>и таким образом уже не могу сравнить ни один
>из них с 3-м.

Кто Вам мешает запомнить эти три элемента?
В условии нет ограничения на запоминание
трех элементов -- там всего лишь есть требование
незавимости используемого объема памяти от
количества элементов в массиве.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]666
2007-06-25 12:32 (ссылка)
понятно, то есть значения элементов доступны

(Ответить) (Уровень выше)


[info]izh
2007-06-25 12:45 (ссылка)
Пардон, не в массиве, а в последовательности.

(Ответить) (Уровень выше)


[info]phantom
2007-06-25 14:18 (ссылка)
что-то никак О(1) не получается.

(Ответить) (Ветвь дискуссии)


[info]izh
2007-06-25 18:03 (ссылка)
Должно получиться.

(Ответить) (Уровень выше)

(Скрытый комментарий)

[info]izh
2007-06-25 18:03 (ссылка)
>потом если A>B - то больше объектов таких же как N0, если B<A - то других Других это каких? Различающихся объектов может быть значительно больше двух. Вот, например, что должен выдать Ваш алгоритм для такой последовательности: 1 2 2 3 3 3

(Ответить) (Уровень выше)


[info]kaledin
2007-06-25 21:33 (ссылка)
Razreshaetsya li derzhat' v pamyati celoe chislo poryadke N?

(Ответить) (Ветвь дискуссии)


[info]izh
2007-06-25 21:51 (ссылка)
Я не очень понял вопрос. В памяти, конечно же
можно держать целое число (или произвольное
фиксированное количество этих чисел), способное
вместить натуральное N (можно для определенности
считать, N помещается в uint32_t, но требование
независимости используемого объема памяти остается
в силе). Понятно, что объем памяти, требуемый под
хранение числа N, зависит от N, но в задаче
конкретно *этой* зависимостью пренебрегаем.

(Ответить) (Уровень выше)


[info]qwerty
2007-06-25 22:32 (ссылка)
Извините, а как ваши объекты идентифицируются? Т.е. каково пространство их идентификаторов? Идентификаторы объектов передаются параметрами вашей функции сравнения на равенство.

Если идентификаторы произвольны, ваша задача не решается, и это очень просто доказать.

(Ответить) (Ветвь дискуссии)


[info]izh
2007-06-26 15:04 (ссылка)
Я не вижу проблем с произвольностью идентификаторов.
Если проблема в том, что от размера идентификатора
зависит объем используемой памяти, то этой
зависимостью можно пренебречь. Существенно, чтобы
используемая память не зависела от количества
элементов в массиве.


Если у Вас есть доказательство нерешаемости задачи
в заданной постановке, я не него с удовольствием
погляжу и скорректирую условие.

(Ответить) (Уровень выше)


[info]666
2007-06-27 14:02 (ссылка)
find_most_repeatable не работает,
например для последовательности x,y,y возвращает x

2й вариант вроде работает

(Ответить) (Ветвь дискуссии)


[info]izh
2007-06-27 15:18 (ссылка)
Спасибо! Поправил.

(Ответить) (Уровень выше) (Ветвь дискуссии)

всё равно не
[info]666
2007-06-27 15:38 (ссылка)
find_most_repeatable_v2( 1125121, 7 ) = 2
find_most_repeatable_v2( 113812131, 9 ) = 3
find_most_repeatable_v2( 11815917151, 11 ) = 5
find_most_repeatable_v2( 1131471917141, 13 ) = 4
find_most_repeatable_v2( 441111711191489, 15 ) = 9
find_most_repeatable_v2( 1551111911155123, 16 ) = 3
find_most_repeatable_v2( 17118131751173171, 17 ) = 7


для теста:

#define N 100
int c,m,i,j,n,a[N];
for(n=1; n<N; n++)
{
for(m=0; m<100000; m++)
{
for(i=0; i<n; i++)
a[i] = 2 + (rand() % 8);
for(i=0; i<(n/2+1); i++)
{
do { j = rand() % n; } while(a[j] == 1 && i < n);
a[j] = 1;
}
c = find_most_repeatable_v2(a,n);
if (c != 1)
{
printf("find_most_repeatable_v2( ");
for(i=0; i<n; i++)
printf("%d",a[i]);
printf(", %d ) = %d\n", n, c);
break;
}
}
}

(Ответить) (Уровень выше) (Ветвь дискуссии)

Re: всё равно не
[info]izh
2007-06-27 16:34 (ссылка)
Я неаккуратный идиот. Спасибо!
Поправил. Ваш тест проходит обе
версии.

(Ответить) (Уровень выше)