Модераторы: Alx, Fixin
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Найти минимальное число, соответствующее исходным данным 
:(
    Опции темы
Akina
Дата 14.12.2007, 21:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Советчик
****


Профиль
Группа: Модератор
Сообщений: 20581
Регистрация: 8.4.2004
Где: Зеленоград

Репутация: 2
Всего: 454



Дано N пар целых чисел Ai,Bi, причем Ai>Bi и нет равных Ai,Aj.
Найти минимальное M, которое при делении на Ai даст в остатке Bi.

Или в олимпиадной трактовке:

Входной файл: в первой строке целое N>1, далее в N строках пары целых Ai,Bi, причем Ai>Bi и нет равных Ai,Aj.
Выходной файл: минимальное положительное M, которое при делении на любое из данных Ai даст в остатке Bi.

Пример:

Входной файл
3
3 1
4 2
7 2

Выходной файл
58

Примечания: 
1) Хоть среди чисел Ai нет равных, это не значит, что все они взаимно просты...
2) Некоторые Bi могут быть равны нулю

Само собой, достаточно описать алгоритм поиска такого M. Плюс алгоритм обнаружения, что входные данные противоречивы.


--------------------
 О(б)суждение моих действий - в соответствующей теме, пожалуйста. Или в РМ. И высшая инстанция - Администрация форума.

PM MAIL WWW ICQ Jabber   Вверх
stab
Дата 15.12.2007, 18:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Экс. модератор
Сообщений: 1839
Регистрация: 1.1.2003

Репутация: нет
Всего: 48



в общем, у меня вот так получилось:

Код

class Program
{
    static void Main(string[] args)
    {
        var sortedByGrowthSpeed = InputPairs.OrderByDescending(pair => pair.A);
        var fastestGrowing = sortedByGrowthSpeed.First();
        var slowlyGrowingPairs = sortedByGrowthSpeed.Skip(1).ToArray();

        var start = slowlyGrowingPairs.All(pair => pair.B == 0) ? 1u : 0u;
        var maxIterations = slowlyGrowingPairs.Aggregate(1u, (total, pair) => total * pair.A);

        var m = 0u;
        var found = false;
        for (var k = start; k <= maxIterations; k++)
        {
            m = (fastestGrowing.A * k) + fastestGrowing.B;
            found = true;
            foreach (var pair in slowlyGrowingPairs)
            {
                if ((m % pair.A) != pair.B)
                {
                    found = false;
                    break;
                }
            }
            if (found)
            {
                break;
            }
        }

        if (found)
        {
            Console.WriteLine("Dear sir, my congratulations, I have found the M = {0}.", m);
        }
        else
        {
            Console.WriteLine("Dear sir, my apologise, there is no solution.");
        }

        Console.ReadLine();
    }

    static readonly InputPair[] InputPairs = new InputPair[]
    {
        new InputPair(3, 1),
        new InputPair(4, 2),
        new InputPair(7, 2)
    };

    struct InputPair
    {
        public InputPair(uint a, uint b)
        {
            A = a;
            B = b;
        }

        public uint A;
        public uint B;
    }
}


наверное, можно как-то умнее, но тогда, видимо, придётся заниматься факторизацией, что тоже трудоёмко и неизвестно отобьёт ли свои затраты.


--------------------
6, 6, 6 - the number of the beast.
PM MAIL WWW   Вверх
Akina
Дата 15.12.2007, 22:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Советчик
****


Профиль
Группа: Модератор
Сообщений: 20581
Регистрация: 8.4.2004
Где: Зеленоград

Репутация: 2
Всего: 454



Перебор - не решение. Пусть и с самым большим возможным шагом. А есмли пойдет речь о  работе в сверхдлинных числах? Программа не отработает до конца, потому что комп в пыль рассыплется от старости.

Добавлено через 8 минут и 14 секунд
Сосбственно суть задачи - именно в поиске зависимостей, способных уйти от перебора или значимо его оптимизировать.


--------------------
 О(б)суждение моих действий - в соответствующей теме, пожалуйста. Или в РМ. И высшая инстанция - Администрация форума.

PM MAIL WWW ICQ Jabber   Вверх
stab
Дата 16.12.2007, 05:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Экс. модератор
Сообщений: 1839
Регистрация: 1.1.2003

Репутация: нет
Всего: 48



для Bi-ых равных нулю, искомое есть произведение простых чисел в наибольшей степени полученных после факторизации всех Ai. по-идее, это можно адаптировать и под ненулевые Bi. перебора нет, но для сверхбольших чисел факторизация - та ещё задачка и от перебора не особо отличается. ничего другого в голову не приходит.


--------------------
6, 6, 6 - the number of the beast.
PM MAIL WWW   Вверх
stab
Дата 16.12.2007, 09:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Экс. модератор
Сообщений: 1839
Регистрация: 1.1.2003

Репутация: нет
Всего: 48



.. оО! кое-что пришло в голову! ща буду проверять, не говори решение.  smile


--------------------
6, 6, 6 - the number of the beast.
PM MAIL WWW   Вверх
Akina
Дата 16.12.2007, 23:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Советчик
****


Профиль
Группа: Модератор
Сообщений: 20581
Регистрация: 8.4.2004
Где: Зеленоград

Репутация: 2
Всего: 454



Цитата(stab @  16.12.2007,  10:27 Найти цитируемый пост)
ща буду проверять, не говори решение

Решение? а его пока нет... есть уже найденные способы вполне серьезной оптимизации - но тем не менее рассчет начинает безбожно тормозить уже на полусотне входных пар.


--------------------
 О(б)суждение моих действий - в соответствующей теме, пожалуйста. Или в РМ. И высшая инстанция - Администрация форума.

PM MAIL WWW ICQ Jabber   Вверх
SelenIT
Дата 17.12.2007, 08:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


баг форума
****


Профиль
Группа: Завсегдатай
Сообщений: 3996
Регистрация: 17.10.2006
Где: Pale Blue Dot

Репутация: 4
Всего: 401



Цитата(Akina @  15.12.2007,  22:23 Найти цитируемый пост)
или значимо его оптимизировать.

Есть вот такая наивная идея: группируем все Ai для каждого из различных Bj. (M - Bj) должно делиться на НОК Ai соответствующей группы (в примере, M-2 должно делиться на 28). Находим среди всех НОК групп наибольшее и пробуем числа-кандидаты с таким шагом, проверяя, выполнятся ли для него остальные условия (делится ли оно на НОК других групп с требуемым остатком). Номера шагов, кратные хотя бы одному из чисел Ai, вошедших в другие группы, очевидно, пропускаем. Также очевидно, что если в какой-то группе хотя бы одно число Ai кратно другому, то достаточно пропускать шаги, кратные минимальному из них. 

Если какое-то из чисел Ai кратно числу Aj из другой группы - входные данные противоречивы (пример - пары входных чисел 4, 2 и 8, 5).

Для приведенного примера: явного признака противоречивости данных нет, есть две группы по остатку. В группе с остатком 1 - 1 число, НОК=3; группа с остатком 2 - 2 числа, НОК = 28. Следовательно, шаг перебора = 28.
Первый кандидат = 28*1+2 = 30. Проверяем, остаток от деления на 3 равен 0, а не 1 - не подходит.
Второй кандидат = 28*2+2 = 58. Проверяем, остаток от деления на 3 равен 1. Ответ найден.

Если бы второй кандидат не подошел, то проверять третьего кандидата не имело бы смысла, т.к. 28*3+2 автоматически не может делиться на 3 с единицей в остатке. Проверять числа, бОльшие, чем НОК всех Ai, имхо тоже нет смысла, потому что ситуация с остатками полностью повторяется с такой периодичностью. Так что достижение этого порога - второй критерий противоречивости входных данных.

Можно ли считать это существенной оптимизацией перебора?

Это сообщение отредактировал(а) SelenIT - 17.12.2007, 08:31


--------------------
Осторожно! Данный юзер и его посты содержат ДГМО! Противопоказано лицам с предрасположенностью к зонеризму!
PM MAIL   Вверх
stab
Дата 17.12.2007, 09:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Экс. модератор
Сообщений: 1839
Регистрация: 1.1.2003

Репутация: нет
Всего: 48



Цитата(Akina @  17.12.2007,  03:37 Найти цитируемый пост)
на полусотне входных пар.

выдай их, плиз, чтобы на одних данных тестировали.


--------------------
6, 6, 6 - the number of the beast.
PM MAIL WWW   Вверх
Akina
Дата 17.12.2007, 10:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Советчик
****


Профиль
Группа: Модератор
Сообщений: 20581
Регистрация: 8.4.2004
Где: Зеленоград

Репутация: 2
Всего: 454



Цитата(SelenIT @  17.12.2007,  09:27 Найти цитируемый пост)
Можно ли считать это существенной оптимизацией перебора?

Это пока одна из двух оптимизаций, давшая существенный эффект. Вторая - разделение Ai на простые делители Aij для каждого Ai,Bi и просчет соотв. Bij. Заодно именно в этот момент происходит проверка валидности входных данных. Построение сетки остатков на пространстве НОД хотя и обещало изрядное ускорение, но на практике для НЕ взаимно простых Ai,Aj оказалось излишне вычислительноемким.

Цитата(stab @  17.12.2007,  10:52 Найти цитируемый пост)
выдай их, плиз, чтобы на одних данных тестировали. 

Реальные данные имеют приблизительно следующие ограничения:
10<N<1000
100<A<1000000
0<B<A/2

Так что вполне можно тестировать скорость на, скажем

Код

for i=1 to 100
  A(i)=Random(10,1000)
  B(i)=Random(0,A(i)/2)
next i


Однако результат гарантированно вылетает за int64...


--------------------
 О(б)суждение моих действий - в соответствующей теме, пожалуйста. Или в РМ. И высшая инстанция - Администрация форума.

PM MAIL WWW ICQ Jabber   Вверх
stab
Дата 17.12.2007, 16:13 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Экс. модератор
Сообщений: 1839
Регистрация: 1.1.2003

Репутация: нет
Всего: 48



фуф, ну кажися сделал:
Код

class Program
{
    static void Main(string[] args)
    {
        //GenerateRandomInput();
        //PrintInputValues();

        Console.WriteLine("Intersecting partial solutions...");
        bool noSolution = false;
        Pair intersection = new Pair();

        DiophantineSolution solution = SolveDiophantineEquation(
            InputPairs[0].A, -InputPairs[1].A,
            InputPairs[1].B - InputPairs[0].B);

        if (solution.HasSolution)
        {
            intersection = new Pair(
                InputPairs[0].A * InputPairs[1].A / solution.Gcd,
                InputPairs[0].A * solution.X0 + InputPairs[0].B);
            Console.WriteLine(intersection);

            for (int i = 1; i < InputPairs.Length; i++)
            {
                solution = SolveDiophantineEquation(
                    intersection.A, -InputPairs[i].A,
                    InputPairs[i].B - intersection.B);

                if (!solution.HasSolution)
                {
                    noSolution = true;
                    break;
                }

                intersection = new Pair(
                    intersection.A * InputPairs[i].A / solution.Gcd,
                    intersection.A * solution.X0 + intersection.B);
                Console.WriteLine(intersection);
            }
        }
        else
        {
            noSolution = true;
        }


        if (noSolution)
        {
            Console.WriteLine("\nDear sir, my apologise, there is no solution.");
        }
        else
        {
            int m = intersection.GetValue(intersection.B == 0 ? 1 : 0);
            Console.WriteLine("\nDear sir, my congratulations, I have found the M = {0}.", m);
        }

        Console.ReadLine();
    }

    static Pair[] InputPairs = new Pair[]
    {
        new Pair(3, 1),
        new Pair(4, 2),
        new Pair(7, 2),
        //new Pair(15, 10),
        //new Pair(101, 5)
    };

    static void PrintInputValues()
    {
        Console.WriteLine("Input pairs: ");
        for (int i = 0; i < InputPairs.Length; i++)
        {
            Console.WriteLine(InputPairs[i]);
        }
        Console.WriteLine();
    }

    static void GenerateRandomInput()
    {
        Random random = new Random();
        InputPairs = new Pair[2 + random.Next(1000)];
        for (int i = 0; i < InputPairs.Length; i++)
        {
            InputPairs[i].A = 1 + random.Next(1000);
            InputPairs[i].B = random.Next(InputPairs[i].A / 2);
        }
    }

    struct Pair
    {
        public Pair(int a, int b)
        {
            A = a;

            if (Math.Abs(b) >= a)
            {
                b %= a;
            }

            if (b >= 0)
            {
                B = b;
            }
            else
            {
                B = a + b;
            }
        }

        public int A;
        public int B;

        public override string ToString()
        {
            return A.ToString() + " * t + " + B.ToString();
        }

        public int GetValue(int t)
        {
            return A * t + B;
        }
    }

    static DiophantineSolution SolveDiophantineEquation(int a, int b, int c)
    {
        GcdExtResult gcdResult = GcdExt(a, b);
        if (gcdResult.Gcd != 0 && c % gcdResult.Gcd == 0)
        {
            c /= gcdResult.Gcd;
            return new DiophantineSolution(true, gcdResult.U * c, gcdResult.V * c,
                gcdResult.Gcd);
        }
        else
        {
            return new DiophantineSolution(false, 0, 0, 0);
        }
    }

    struct DiophantineSolution
    {
        public DiophantineSolution(bool hasSolution, int x0, int y0, int gcd)
        {
            HasSolution = hasSolution;
            X0 = x0;
            Y0 = y0;
            Gcd = gcd;
        }

        public bool HasSolution;
        public int X0;
        public int Y0;
        public int Gcd;

        public override string ToString()
        {
            return string.Format("{0}, {1}, {2}", HasSolution, X0, Y0);
        }
    }

    static GcdExtResult GcdExt(int a, int b)
    {
        int x1 = 0;
        int y1 = 0;
        int x2 = 0;
        int y2 = 0;
        int x3 = 0;
        int y3 = 0;
        int q = 0;
        int r = 0;
        int sa = 0;
        int sb = 0;

        if (b != 0 & a != 0)
        {
            x2 = 1;
            x1 = 0;
            y2 = 0;
            y1 = 1;
            sa = Math.Sign(a);
            sb = Math.Sign(b);
            a = Math.Abs(a);
            b = Math.Abs(b);
            while (b > 0)
            {
                q = a / b;
                r = a - q * b;
                x3 = x2 - q * x1;
                y3 = y2 - q * y1;
                a = b;
                b = r;
                x2 = x1;
                x1 = x3;
                y2 = y1;
                y1 = y3;
            }
            return new GcdExtResult(a, sa * x2, sb * y2);
        }
        else
        {
            return new GcdExtResult(0, 0, 0);
        }
    }

    struct GcdExtResult
    {
        public GcdExtResult(int gcd, int u, int v)
        {
            Gcd = gcd;
            U = u;
            V = v;
        }

        public int Gcd;
        public int U;
        public int V;

        public override string ToString()
        {
            return string.Format("{0}, {1}, {2}", Gcd, U, V);
        }
    }
}


вкратце, идея такова, происходит решение линейных диафантовых уравнений с двумя неизвестными, т.е. решение аналитическое. каждое решение позволяет создать пересечение решений уже решённых уравнений и очередной пары, и в итоге это приводит (или не приводит при отсутствии решений\пересечений) к одной паре At + B, M = B при t = 0, или при B равном 0, М = А, что требуется по условию задачи. трудоёмкость алгоритма, как можно заметить, пропорциональна N, но оптимизировать ещё много куда есть. smile

спасибо, хорошая задачка. основательно подучил теорию чисел, за два дня с нуля.  smile


--------------------
6, 6, 6 - the number of the beast.
PM MAIL WWW   Вверх
Akina
Дата 17.12.2007, 16:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Советчик
****


Профиль
Группа: Модератор
Сообщений: 20581
Регистрация: 8.4.2004
Где: Зеленоград

Репутация: 2
Всего: 454



Цитата(stab @  17.12.2007,  17:13 Найти цитируемый пост)
вкратце, идея такова

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


--------------------
 О(б)суждение моих действий - в соответствующей теме, пожалуйста. Или в РМ. И высшая инстанция - Администрация форума.

PM MAIL WWW ICQ Jabber   Вверх
stab
Дата 17.12.2007, 16:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Экс. модератор
Сообщений: 1839
Регистрация: 1.1.2003

Репутация: нет
Всего: 48



это C#, он попроще немного. smile


--------------------
6, 6, 6 - the number of the beast.
PM MAIL WWW   Вверх
stab
Дата 17.12.2007, 17:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Экс. модератор
Сообщений: 1839
Регистрация: 1.1.2003

Репутация: нет
Всего: 48



.. поподробнее. нужно решить систему уравнений в целых числах:

A1 * X1 + B1 = M
A2 * X2 + B2 = M
..
Ai * Xi + Bi = M

неизвестные: Xi и М. система недопределена, поэтому решений, если таковые имеются, множество. как это водится для таких систем будем решать следующим образом, избавимся от M:

A1 * X1 + B1 = A2 * X2 + B2

замечаем, что это обычное линейное диафантово уравнение с двумя неизвестными:

A1 * X1 - A2 * X2 = B2 - B1

его решения выражаются, в данном случае, как:

X1 = Z1 + A2 * t
X2 = Z2 + A1 * t

Z1 = (B2 - B1) * U и Z2 = (B2 - B1) * V, U и V - коэффициенты Безу. самое замечательное, что после решения этого уравнения X1 и X2 выражаются через одну переменную t, подставив в исходное уравнение получаем:

A1 * (Z1 + A2 * t) + B1 = A2 * (Z2 + A1 * t) + B2
A1 * A2 * t + A1 * Z1 + B1 = A1 * A2 * t + A2 * Z2 + B2 - что является верным тождеством.

выходит, что уравнение с двумя неизвестными мы свели к одному с одним неизвестным t и теперь вправе воспользоваться одной из частей тождества. дальше всё просто:

A1 * A2 * t + A1 * Z1 + B1 = A3 * X3 + B3

решаем тем же способом, выражаем через одну переменную, и так далее в том же духе для всех Ai * Xi + Bi. при подстановке реальных чисел главное не забывать сокращать коэффициент при t на наибольший общий делитель, полученный при вычислении U и V, и приводить данные к нормализованному виду - при необходимости, A1 * Z1 + B1 делить по модулю на A1 * A2 и выражать в положительных числах по тому же модулю.

в итоге, получается одно уравнение-решение всей системы, которое является пересечением всех индивидуальных решений исходных уравнений в исходной системе:

AF * t + BF = M

т.к. мы решаем в целых числах и BF < AF и AF > 0 (из природы данных), то M = AF * 0 + BF = BF или M = AF * 1 при BF = 0.


--------------------
6, 6, 6 - the number of the beast.
PM MAIL WWW   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Интересные и занимательные задачи по программированию | Следующая тема »


 




[ Время генерации скрипта: 0.1013 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.