LINUX.ORG.RU

Помогите решить.

 , ,


1

4

Если лень читать - надо быстро посчитать:

 ( 3877^n)- 1) / ( 3877 - 1 ) mod 139968, где n - числа порядка 10^9.




Предыстория. Тут недавно в новостях в новости про раст разговор зашел про бенчмарки, а конкретно про fasta(http://benchmarksgame.alioth.debian.org/u64q/fasta-description.html#fasta), но это не важно.

Решил я значит тут запилить масштабируемый lc-генератор. Я искал в интернетах что-то подобное - нашел только: https://github.com/johannesgerer/jburkardt-c/blob/afff376e712b07088c09e729e89... но оно не работает и полезного я нашёл там только:

SEED(N) = a^N * SEED + ( a^N - 1) / ( a - 1 ) * b

Но до этого я смог дойти сам. В конечном итоге я всё запилил - всё работает, но при распараллеливании возникла проблема.

Надо инициализировать каждый воркер значением: a^N mod m и ( a^N - 1) / ( a - 1 ) mod m.

Посчитать быстро a^N mod m - труда не составляет, ибо a^(N/ 2) ^ 2 = a^N. Но вот как посчитать ( a^125000000 - 1) / ( a - 1 ) mod m - я так и не осилил.

Сейчас использую кастыль(но он работает дольше чем генерация 250кк чисел на одном ведре):

double mod_139968(double n) {
  return n + (floor(n * 1. / 139968) * -139968);
}


double calc_sumpown(uint64_t n) {
  double sum = 1, apown = 1;
  while(--n) {
    sum += (apown = mod_139968(apown * 3877));
  }
  sum = mod_139968(sum);
  return sum;
}

Я уже сижу над этим дня 3-4 и перегуглил всё что мог - не помогло. Я даже не могу придумать/найти ключевые слова чтобы что-то на эту тему нагуглить.

По ссылке выше есть https://github.com/johannesgerer/jburkardt-c/blob/afff376e712b07088c09e729e89..., но она не работает, а что конкретно она там решает - я не понимаю.

Не вникал в историю треда (по ссылкам не ходил).

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

bvn13 ★★★★★
()
Ответ на: комментарий от jtootf

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

registrant27492
() автор топика
Ответ на: комментарий от bvn13

вольфрам-математика

У меня нет вольфрам-математики

нормально подобное проглотит

Нормально проглотит - это как? Решит? Покажет как решать?

registrant27492
() автор топика

Я думаю надо использовать факт, что

(a^n-1)/(a-1)=1+a^2+...+a^{n-1}.

и теорему Эйлера.

dikiy ★★☆☆☆
()
Последнее исправление: dikiy (всего исправлений: 1)

короче - теорема Эйлера как раз то, что тебе надо. Зыри в википедии.

В двух словах твой алгоритм таков:


1. так как НОД(3877,139968)=1 теорема Эйлера применима.

2. функция Эйлера от 139968 равна 37 и по теореме имеем 3877^37=1 mod 139968

3. переводим уравнение в форму 1+a^2+...+a^{n-1} mod n.

4. теперь просто применяем факт что

3877^{x+37k} mod 139968 = 3877^x mod 139968.

dikiy ★★☆☆☆
()
Ответ на: комментарий от invy

Мне не надо «fast modulo exponentiation». Мне надо сумму степеней числа до n. Как это гуглить - я не знаю. Можешь подсказать как это называется, если знаешь.

registrant27492
() автор топика

( a^125000000 - 1) / ( a - 1 ) mod m - я так и не осилил.

что тут непонятного?

(a + b) mod n = (a mod n + b mod n) mod n
(a - b) mod n = (a mod n - b mod n) mod n
(a * b) mod n = (a mod n * b mod n) mod n
invy ★★★★★
()
Последнее исправление: invy (всего исправлений: 1)
Ответ на: комментарий от dikiy

Спасибо. Но не понятно.

3877^{x+37k} mod 139968 = 3877^x mod 139968.

А что за k?

переводим уравнение в форму 1+a^2+...+a^{n-1} mod n.

Что значит переводим? Как можно поменять m на n?

registrant27492
() автор топика
Ответ на: комментарий от registrant27492

А что за k?

любое целое.

Что значит переводим? Как можно поменять m на n?

опечатался я:

1+a^2+...+a^{n-1} mod 139968.

но это тупняк в ряд разлагать походу :)

посчитай быстро 3877^n mod 139968. А потом просто используй правила сложения и умножения по модулю, как выше описали.

dikiy ★★☆☆☆
()
Последнее исправление: dikiy (всего исправлений: 1)
Ответ на: комментарий от registrant27492

берем n. Считаем остаток r от деления на 37.

Теперь считаем b = 3877^r mod 139968.

теперь искомое число это (b - 1)/(3877-1);

dikiy ★★☆☆☆
()
Ответ на: комментарий от dikiy

посчитай быстро 3877^n mod 139968. А потом просто используй правила сложения и умножения по модулю, как выше описали.

Использовать как? Сложить 10^9 раз в цикле?

https://github.com/johannesgerer/jburkardt-c/blob/afff376e712b07088c09e729e89... - вот тут пацаны пишут:

SEED(N) = a^N * SEED + ( a^N - 1) / ( a - 1 ) * b
              = AN * SEED + BN
/*
  Solve 
    ( a - 1 ) * BN = ( a^N - 1 ) mod B
  for BN.

*/
solves a congruence of the form A * X = C ( mod B ).
registrant27492
() автор топика
Ответ на: комментарий от dikiy

Хорошо. Берём n = 55;

Считаем остаток r от деления на 37.

55 mod 37 = 18;

Теперь считаем b = 3877^r mod 139968.

3877^18 mod 139968 = 128089;

теперь искомое число это (b - 1)/(3877-1);

(128089 - 1)/(3877-1) = 33.046

Решение: (3877^55 - 1)/(3877-1) mod 139968 = 78139

Я всё никак не могу это сопоставить с решением. И 3877^37 mod 139968 никак не один. И равна каждая 37-я степень по определению быть не может, ибо числа все равномерное распределены по 0...139968.

Прости. Слишком непонятно.

registrant27492
() автор топика
Ответ на: комментарий от dikiy

155 mod 128 = 18;

27

Теперь считаем b = 3877^r mod 139968.

3877^27 mod 139968 = 139645;

теперь искомое число это (b - 1)/(3877-1);

(139645 - 1)/(3877-1) = 36.027863777

Решение: (3877^155 - 1)/(3877-1) mod 139968 = 96263

registrant27492
() автор топика
Ответ на: комментарий от registrant27492

так, щас будем модифицировать :)

dikiy ★★☆☆☆
()
Ответ на: комментарий от registrant27492

я неправильно посчитал функцию эйлера от 139968. Это 46656. Так что не очень хорошо, но все же норм.

теперь r:=n mod 46656.

И получается

(a^n-1)/(a-1) mod 139968 = (a^r-1)/(a-1) mod 139968.

dikiy ★★☆☆☆
()
Последнее исправление: dikiy (всего исправлений: 1)
Ответ на: комментарий от f1u77y

возводишь в степень по модулю за O(log n), затем делишь по модулю, вроде несложно

нифига не O(log n), а O(C), хехе :D

dikiy ★★☆☆☆
()
Ответ на: комментарий от dikiy

но с такими числами нельзя пользоваться конечно стандартными библиотеками. Тебе нужна либа с длинной арифметикой. Или пистон какой-нить. Так-то числа такой длины (вплоть до 3877^1000000) будут вполне удобоваримые для них. У тебя экспонента в самом худшем случае <50000

отпишись хоть, помогло ли.

dikiy ★★☆☆☆
()
Последнее исправление: dikiy (всего исправлений: 1)
Ответ на: комментарий от registrant27492

Пусть x/y = n mod(z). x_z = x mod(z), y_z = y mod(z), x_z, y_z < z.

x = y*n, x_z = y*n, x_z = y_z*n всё mod(z).

x_z + k*z = y_z*n, k<z т.к. y_z, n < z

n = (x_z + k*z)/y_z

tyakos ★★★
()

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

Надо вычислить (X/Y) mod Z, где X большое и сложно вычисляемое, а Y и Z не очень большие

Имеем (X/Y) mod Z == ((X + tYZ)/Y) mod Z для любого целого t.

А значит выполнено (X/Y) mod Z == ((X mod YZ)/Y) mod Z

Получается что можно считать как

(( 3877^n mod 139968*3876)-1) / ( 3877 - 1 ) mod 139968

Я где-то ошибся?

GPFault ★★
()
Ответ на: комментарий от GPFault

ибо тоже сходу не вижу в чём проблема.

Проблема в том,что я - пту, не?

Я где-то ошибся?

Нет - спасибо. То что нужно.

registrant27492
() автор топика
Ответ на: комментарий от registrant27492

я хотел делить остаток по модулю. в любом случае, проблема в том, что (a / b) % c не вычисляется нормально, когда (b, c) != 1, так что пока вижу лишь решение от дикого

f1u77y ★★★★
()
Ответ на: комментарий от f1u77y
uint64_t calc_sumpown(uint64_t n) {
  return (mod_pow(3877, n, 139968 * 3876) - 1) / 3876;
}

Это работает - идеально.

registrant27492
() автор топика
Ответ на: комментарий от dikiy

Тебе нужна либа с длинной арифметикой. Или пистон какой-нить.

Попробовал - пистон считает в 10раз дольше, чем у меня гинерируется

for(n = 0...250000000)
  (3877 ^ n * 42 + ((3877 ^ n - 1) / (3877 - 1)) * 29573) (mod m)

«Моим» методом - простым сложением всех степеней.

отпишись хоть, помогло ли.

Это вряд-ли поможет, но решение Помогите решить. (комментарий) - работает.

Вроде как проблема решена. Спасибо.

registrant27492
() автор топика
Ответ на: комментарий от f1u77y

Не знаю, я сделал:

double calc_sumpown(uint64_t n) {
  uint64_t i = 0;
  double sum = 1, apown = 1;
  while(++i != n) {
    uint32_t a = sum, b = calc_sumpown_new(i);
    if(a != b) {fprintf(stderr, "!%lu: %u -> %u\n", i, a, b); exit(1);}
    sum += (apown = mod_139968(apown * 3877));
    sum = mod_139968(sum);
  }
  return sum;
}
calc_sumpown(250000000);

Все результаты для a^0...250000000 совпадают со 100% правильной функцией, которая просто складывает степени.

Поставил для 25000000000 - минут через 5-10посчитает.

registrant27492
() автор топика
Ответ на: комментарий от registrant27492

Упало на: !4294967296: 117760 -> 0

Но это тупо переполнились 32битные чиселки. Так что всё работает.

registrant27492
() автор топика

Спасибо всем. Пока проблема решена - в следующей теме напишу что получилось в конечном итоге.

registrant27492
() автор топика
Ответ на: комментарий от registrant27492

Тебе нужна либа с длинной арифметикой. Или пистон какой-нить.

Попробовал - пистон считает в 10раз дольше, чем у меня гинерируется

for(n = 0...250000000)

што ж ты творишь :D

надо 250000000 делить на 46656. Это 17152. И цикл только до 17152 надо гнать.

dikiy ★★☆☆☆
()
Ответ на: комментарий от f1u77y

с таким phi(n) логарифм явно лучше. или ты про что другое?

меня терзают сомнения, что улучшить константу нельзя все равно.

dikiy ★★☆☆☆
()
Ответ на: комментарий от aiqu6Ait

воспользуйся Maxima.

попробовал ради интереса. Как я и ожидал maxima всосала с бульканьем. Даже тестовые 3877^1000000 посчитать не смогла.

dikiy ★★☆☆☆
()
Последнее исправление: dikiy (всего исправлений: 1)
Ответ на: комментарий от f1u77y

константу улучшить нельзя, но можно считать за логарифм с маленькой константой без функции эйлера

а как ты в степено собрался возводить без Эйлера-то?

dikiy ★★☆☆☆
()
Ответ на: комментарий от dikiy

што ж ты творишь :D

Нет, ты не понял - это считаю я, а не пистон. Пистон считает за это время только одно (3877 ** 100000) % 139968.

for(n = 0...250000000)
  (3877 ^ n * 42 + ((3877 ^ n - 1) / (3877 - 1)) * 29573) (mod m)

Этот цикл на одном ведре у меня генерирует 250000000 значений за <0.5sec. А вычисление этого мне нужно - чтобы считать это в параллель - соседняя тема раскрывает суть.

Но твоё решение надо будет применить к основному циклу, если оно применимо. Надо подумать над этим.

registrant27492
() автор топика
Ответ на: комментарий от registrant27492

Но твоё решение надо будет применить к основному циклу, если оно применимо. Надо подумать над этим.

Применимо и легко. Ты ищешь i s.t 3877^i = 1 mod 139968. Оно у тебя есть. Всё, от любой огромной степени остаётся число <i. Поделить можешь, используя мой коммент, прогоняя по k от 1 до z.

tyakos ★★★
()
Ответ на: комментарий от f1u77y

константу улучшить нельзя, но можно считать за логарифм с маленькой константой без функции эйлера

только что подумал - эту r саму можно по идее ТС как (N/2)*2 представлять, с попутным отниманием единицы:

то есть если r нечетное:

3877^r mod c = ([3877^((r-1)/2) mod c]^2 * 3877 ) mod c.

dikiy ★★☆☆☆
()
Ответ на: комментарий от registrant27492

Она неизвестна. Но ты знаешь, что она <z. Прогони от 1 до z, как я написал выше. Тебе нужно целое число.

tyakos ★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.