Компилятор даёт значение 48. Калькулятор 3 - правильное значение. Не совсем пойму - в чём проблема. Возможно, слишком большие числа выходят. Как с этим справиться? Заранее спасибо Вам.
-
% - это операция деления? В шарпе так остаток считается - https://docs.microsoft.com/ru-ru/dotnet/csharp/language-reference/operators/ – Monk Nov 29 '17 at 15:49
-
Верно ли понимаю что вручную получилось 3,3? – V.March Nov 29 '17 at 15:57
-
Да, это остаток от деления. Но это не меняет сути – Nov 29 '17 at 15:58
-
Вручную получилось просто 3 – Nov 29 '17 at 15:58
2 Answers
31^43 это число с 64 десятеричными знаками, тип decimal или double не способен столько хранить. Вы можете использовать длинную арифметику, что бы посчитать такое. Например встроенный тип BigInteger с System.Numerics.dll:
Console.WriteLine(BigInteger.ModPow(31, 43, 77));
Выведет 3. Более того, этот метод хорошо оптимизирован.
- 11,345
Дело в том, что Math.Pow работает с типом double, а у него ограниченная точность: он хранит число с точностью в 52 бита, а значит, у больших чисел младшие разряды получаются неточными. А для модуля нужны именно младшие разряды, а старшие не нужны вовсе.
Если бы Math.Pow работал с int, проблема была бы та же: int больше двух с копейками миллиардов будет взят по модулю 2^32, а это явно не то, что вам нужно.
Что же делать? А нужно выполнять операции не настолько в лоб. Вы должны представить возведение в степень как несколько умножений, и при каждом умножении брать модуль от результата.
Получится что-то такое:
uint multmod(uint a, uint b, uint mod) => (uint)(((ulong)a * b) % mod);
uint powermod(uint n, uint pow, uint mod)
{
uint result = 1;
uint npow = n;
while (pow != 0)
{
if (pow % 2 == 1)
result = multmod(result, npow, mod);
pow = pow / 2;
npow = multmod(npow, npow, mod);
}
return result;
}
Какой из методов лучше: считать вручную или воспользоваться библиотечным методом BigInteger.ModPow? Вопрос не так уж и тривиален.
С одной стороны, если вычислений немного, то лучше воспользоваться проверенным и отлаженным библиотечным методом. Скорость выполнения составляет величину порядка нескольких сотен наносекунд, это реально очень быстро, так что об этом можно не беспокоиться.
С другой стороны, если вычислений реально много, более миллиона в секунду, то нанооптимизации начинают иметь значение. Какой из методов быстрее: библиотечный или ручной? За библиотечный метод говорит то, что его писали и отлаживали профессионалы, и то, что в новых версиях фреймворка он наверняка ещё будет улучшаться. За ручной метод говорит то, что библиотечный метод слишком общ (он считает числа произвольной величины!), и за счёт этого может делать и слишком много. Тестируем!
Я написал вот такой тест на BenchmarkDotNet:
class Program
{
static void Main(string[] args)
{
Console.ReadKey();
var summary = BenchmarkRunner.Run<ModPowComparison>();
Console.ReadKey();
}
}
public class ModPowComparison
{
[Params(3, 31, 8181818)]
public uint A;
[Params(3, 43, 243)]
public uint B;
[Params(2, 77, 1024, 100000)]
public uint C;
[Benchmark(Baseline=true)]
public BigInteger BigNum() => BigInteger.ModPow(A, B, C);
[Benchmark]
public BigInteger Manual() => powermod(A, B, C);
uint multmod(uint a, uint b, uint mod) => (uint)(((ulong)a * b) % mod);
uint powermod(uint n, uint pow, uint mod)
{
uint result = 1;
uint npow = n;
while (pow != 0)
{
if (pow % 2 == 1)
result = multmod(result, npow, mod);
pow = pow / 2;
npow = multmod(npow, npow, mod);
}
return result;
}
}
Я взял немного маленьких чисел и немного чисел побольше. Вот результирующая таблица:
BenchmarkDotNet=v0.10.10, OS=Windows 7 SP1 (6.1.7601.0)
Processor=Intel Core i7-2600K CPU 3.40GHz (Sandy Bridge), ProcessorCount=8
Frequency=3320371 Hz, Resolution=301.1712 ns, Timer=TSC
[Host] : .NET Framework 4.7 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.2053.0
DefaultJob : .NET Framework 4.7 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.2053.0
Method | A | B | C | Mean | Error | StdDev | Median | Scaled |
------- |-------- |---- |------- |----------:|-----------:|-----------:|----------:|-------:|
BigNum | 3 | 3 | 2 | 149.01 ns | 0.3356 ns | 0.2975 ns | 149.08 ns | 1.00 |
Manual | 3 | 3 | 2 | 32.51 ns | 0.0581 ns | 0.0544 ns | 32.52 ns | 0.22 |
BigNum | 3 | 3 | 77 | 149.08 ns | 0.4919 ns | 0.4602 ns | 149.17 ns | 1.00 |
Manual | 3 | 3 | 77 | 33.00 ns | 0.1314 ns | 0.1229 ns | 32.99 ns | 0.22 |
BigNum | 3 | 3 | 1024 | 149.17 ns | 0.2275 ns | 0.2128 ns | 149.15 ns | 1.00 |
Manual | 3 | 3 | 1024 | 33.18 ns | 0.0888 ns | 0.0831 ns | 33.17 ns | 0.22 |
BigNum | 3 | 3 | 100000 | 149.02 ns | 0.5999 ns | 0.5318 ns | 148.99 ns | 1.00 |
Manual | 3 | 3 | 100000 | 33.17 ns | 0.0549 ns | 0.0514 ns | 33.18 ns | 0.22 |
BigNum | 3 | 43 | 2 | 262.27 ns | 0.5958 ns | 0.5573 ns | 262.23 ns | 1.00 |
Manual | 3 | 43 | 2 | 76.55 ns | 0.1985 ns | 0.1856 ns | 76.56 ns | 0.29 |
BigNum | 3 | 43 | 77 | 262.12 ns | 0.5056 ns | 0.4729 ns | 262.14 ns | 1.00 |
Manual | 3 | 43 | 77 | 78.53 ns | 0.2021 ns | 0.1791 ns | 78.56 ns | 0.30 |
BigNum | 3 | 43 | 1024 | 262.48 ns | 0.7268 ns | 0.6798 ns | 262.42 ns | 1.00 |
Manual | 3 | 43 | 1024 | 78.66 ns | 0.2674 ns | 0.2502 ns | 78.67 ns | 0.30 |
BigNum | 3 | 43 | 100000 | 262.35 ns | 0.7756 ns | 0.7255 ns | 262.26 ns | 1.00 |
Manual | 3 | 43 | 100000 | 79.13 ns | 0.1902 ns | 0.1779 ns | 79.21 ns | 0.30 |
BigNum | 3 | 243 | 2 | 337.59 ns | 0.8698 ns | 0.8136 ns | 337.52 ns | 1.00 |
Manual | 3 | 243 | 2 | 114.33 ns | 10.2557 ns | 16.2666 ns | 104.97 ns | 0.34 |
BigNum | 3 | 243 | 77 | 337.43 ns | 0.8991 ns | 0.8410 ns | 337.53 ns | 1.00 |
Manual | 3 | 243 | 77 | 106.63 ns | 0.2987 ns | 0.2648 ns | 106.62 ns | 0.32 |
BigNum | 3 | 243 | 1024 | 337.65 ns | 0.5805 ns | 0.5430 ns | 337.59 ns | 1.00 |
Manual | 3 | 243 | 1024 | 107.24 ns | 0.1823 ns | 0.1705 ns | 107.24 ns | 0.32 |
BigNum | 3 | 243 | 100000 | 362.40 ns | 0.9287 ns | 0.7755 ns | 362.21 ns | 1.00 |
Manual | 3 | 243 | 100000 | 106.85 ns | 0.1600 ns | 0.1419 ns | 106.86 ns | 0.29 |
BigNum | 31 | 3 | 2 | 149.07 ns | 0.5325 ns | 0.4720 ns | 149.04 ns | 1.00 |
Manual | 31 | 3 | 2 | 32.54 ns | 0.0861 ns | 0.0805 ns | 32.53 ns | 0.22 |
BigNum | 31 | 3 | 77 | 149.45 ns | 0.5391 ns | 0.4779 ns | 149.47 ns | 1.00 |
Manual | 31 | 3 | 77 | 32.73 ns | 0.1518 ns | 0.1420 ns | 32.71 ns | 0.22 |
BigNum | 31 | 3 | 1024 | 150.40 ns | 0.4437 ns | 0.3934 ns | 150.32 ns | 1.00 |
Manual | 31 | 3 | 1024 | 33.11 ns | 0.0672 ns | 0.0628 ns | 33.12 ns | 0.22 |
BigNum | 31 | 3 | 100000 | 150.89 ns | 0.7241 ns | 0.6047 ns | 150.65 ns | 1.00 |
Manual | 31 | 3 | 100000 | 33.42 ns | 0.3659 ns | 0.3422 ns | 33.25 ns | 0.22 |
BigNum | 31 | 43 | 2 | 264.31 ns | 1.2552 ns | 1.1741 ns | 264.45 ns | 1.00 |
Manual | 31 | 43 | 2 | 76.87 ns | 0.2282 ns | 0.2134 ns | 76.91 ns | 0.29 |
BigNum | 31 | 43 | 77 | 262.70 ns | 1.0268 ns | 0.9605 ns | 262.56 ns | 1.00 |
Manual | 31 | 43 | 77 | 79.13 ns | 0.2694 ns | 0.2520 ns | 79.08 ns | 0.30 |
BigNum | 31 | 43 | 1024 | 263.40 ns | 1.3053 ns | 1.2209 ns | 262.74 ns | 1.00 |
Manual | 31 | 43 | 1024 | 78.98 ns | 0.1876 ns | 0.1755 ns | 78.94 ns | 0.30 |
BigNum | 31 | 43 | 100000 | 262.73 ns | 0.7095 ns | 0.6290 ns | 262.66 ns | 1.00 |
Manual | 31 | 43 | 100000 | 78.99 ns | 0.1747 ns | 0.1634 ns | 78.95 ns | 0.30 |
BigNum | 31 | 243 | 2 | 338.05 ns | 1.4928 ns | 1.3964 ns | 337.56 ns | 1.00 |
Manual | 31 | 243 | 2 | 105.00 ns | 0.1658 ns | 0.1551 ns | 104.97 ns | 0.31 |
BigNum | 31 | 243 | 77 | 337.59 ns | 0.7197 ns | 0.6732 ns | 337.53 ns | 1.00 |
Manual | 31 | 243 | 77 | 106.78 ns | 0.2404 ns | 0.2131 ns | 106.76 ns | 0.32 |
BigNum | 31 | 243 | 1024 | 338.06 ns | 0.6610 ns | 0.6183 ns | 337.81 ns | 1.00 |
Manual | 31 | 243 | 1024 | 106.51 ns | 0.3239 ns | 0.3030 ns | 106.44 ns | 0.32 |
BigNum | 31 | 243 | 100000 | 385.20 ns | 0.7483 ns | 0.7000 ns | 385.16 ns | 1.00 |
Manual | 31 | 243 | 100000 | 107.31 ns | 0.2380 ns | 0.2227 ns | 107.27 ns | 0.28 |
BigNum | 8181818 | 3 | 2 | 173.17 ns | 0.4454 ns | 0.4166 ns | 173.23 ns | 1.00 |
Manual | 8181818 | 3 | 2 | 34.74 ns | 0.0799 ns | 0.0747 ns | 34.75 ns | 0.20 |
BigNum | 8181818 | 3 | 77 | 172.74 ns | 0.1622 ns | 0.1267 ns | 172.76 ns | 1.00 |
Manual | 8181818 | 3 | 77 | 33.24 ns | 0.0937 ns | 0.0876 ns | 33.24 ns | 0.19 |
BigNum | 8181818 | 3 | 1024 | 172.72 ns | 0.3758 ns | 0.3515 ns | 172.73 ns | 1.00 |
Manual | 8181818 | 3 | 1024 | 32.68 ns | 0.0712 ns | 0.0631 ns | 32.68 ns | 0.19 |
BigNum | 8181818 | 3 | 100000 | 197.96 ns | 0.4350 ns | 0.4069 ns | 197.88 ns | 1.00 |
Manual | 8181818 | 3 | 100000 | 33.12 ns | 0.1025 ns | 0.0959 ns | 33.11 ns | 0.17 |
BigNum | 8181818 | 43 | 2 | 287.34 ns | 0.6073 ns | 0.5681 ns | 287.18 ns | 1.00 |
Manual | 8181818 | 43 | 2 | 77.66 ns | 0.1319 ns | 0.1233 ns | 77.66 ns | 0.27 |
BigNum | 8181818 | 43 | 77 | 287.11 ns | 0.9016 ns | 0.8434 ns | 287.30 ns | 1.00 |
Manual | 8181818 | 43 | 77 | 80.28 ns | 0.1037 ns | 0.0919 ns | 80.28 ns | 0.28 |
BigNum | 8181818 | 43 | 1024 | 287.15 ns | 0.7190 ns | 0.6725 ns | 287.25 ns | 1.00 |
Manual | 8181818 | 43 | 1024 | 78.77 ns | 0.1394 ns | 0.1304 ns | 78.79 ns | 0.27 |
BigNum | 8181818 | 43 | 100000 | 401.59 ns | 0.7218 ns | 0.6398 ns | 401.37 ns | 1.00 |
Manual | 8181818 | 43 | 100000 | 79.45 ns | 0.1356 ns | 0.1202 ns | 79.39 ns | 0.20 |
BigNum | 8181818 | 243 | 2 | 362.07 ns | 0.6777 ns | 0.6339 ns | 361.93 ns | 1.00 |
Manual | 8181818 | 243 | 2 | 105.72 ns | 0.2266 ns | 0.2119 ns | 105.77 ns | 0.29 |
BigNum | 8181818 | 243 | 77 | 362.05 ns | 0.4713 ns | 0.3936 ns | 362.06 ns | 1.00 |
Manual | 8181818 | 243 | 77 | 108.33 ns | 0.1698 ns | 0.1589 ns | 108.29 ns | 0.30 |
BigNum | 8181818 | 243 | 1024 | 362.43 ns | 0.8363 ns | 0.7823 ns | 362.21 ns | 1.00 |
Manual | 8181818 | 243 | 1024 | 104.96 ns | 0.2377 ns | 0.2224 ns | 104.95 ns | 0.29 |
BigNum | 8181818 | 243 | 100000 | 506.07 ns | 1.1124 ns | 0.9289 ns | 506.21 ns | 1.00 |
Manual | 8181818 | 243 | 100000 | 107.34 ns | 0.3394 ns | 0.3175 ns | 107.37 ns | 0.21 |
Тут интересна последняя колонка, которая показывает относительную скорость. Из неё видно, что в данных тестовых условиях ручной метод по времени занимает от 17 до 32% времени, необходимого библиотечному методу.
В других условиях результаты будут, конечно, другими.
- 206,799
-
-
-
странная реализация, я в ней просто запуталсо) npow - это не степень... Привычнее видеть что-то вроде
powmod(powmod(n, pow/2, mod), 2, m)– vp_arth Nov 29 '17 at 17:15 -