/// <summary> /// Вычитание с учетом знака /// </summary> /// <param name="value">Вычитаемое</param> /// <returns>Разность</returns> public BigInteger Substract(BigInteger value) { if (this.sign && value.sign) { return(this.AbsSubstract(value)); } else if (!this.sign && value.sign) { BigInteger ans = this; ans.ChangeSign(true); ans = ans.AbsAdd(value); ans.ChangeSign(false); return(ans); } else if (this.sign && !value.sign) { BigInteger ans = value; ans.ChangeSign(true); ans = this.AbsAdd(ans); return(ans); } else { BigInteger ans1 = value; BigInteger ans2 = this; ans1.ChangeSign(true); ans2.ChangeSign(true); ans1 = ans1.Substract(ans2); return(ans1); } }
/// <summary> /// Делим большое число на большое с учетом знака /// </summary> /// <param name="q">Частное q[m]..q[0]</param> /// <param name="r">Остаток r[n - 1]..r[0]</param> /// <param name="u">u[m + n - 1]..u[0] — делимое по основанию b</param> /// <param name="v">v[n - 1]..v[0] — делитель по основанию b</param> /// <returns></returns> private static int Divide(out BigInteger q, out BigInteger r, BigInteger u, BigInteger v) { int ans = AbsDivide(out q, out r, u, v); if ((u.sign && v.sign) || (!u.sign && !v.sign)) { return(ans); } q.ChangeSign(!q.sign); r.ChangeSign(!r.sign); return(ans); }
/// <summary> /// Умножение с учетом знака /// </summary> /// <param name="value">На что умножаем</param> /// <returns>Произведение</returns> public BigInteger Multiply(int value) { BigInteger ans = this.AbsMultiply(Math.Abs(value)); if (this.sign && value >= 0 || !this.sign && value < 0) { return(ans); } ans.ChangeSign(false); return(ans); }
/// <summary> /// Умножение больших чисел с учетом знака /// </summary> /// <param name="value">На что умножаем</param> /// <returns>Произведение</returns> public BigInteger Multiply(BigInteger value) { BigInteger a = this; BigInteger b = value; BigInteger ans; if (a.sign && b.sign) { return(a.AbsMultiply(b)); } else if (!a.sign && !b.sign) { a.ChangeSign(true); b.ChangeSign(true); ans = a.AbsMultiply(b); a.ChangeSign(false); b.ChangeSign(false); return(ans); } else if (!a.sign) { a.ChangeSign(true); b = a.AbsMultiply(b); b.sign = false; a.ChangeSign(false); return(b); } else { b.sign = true; a = a.AbsMultiply(b); a.sign = false; b.sign = false; return(a); } }
/// <summary> /// Деление большого числа на маленькое с учетом знака /// </summary> /// <param name="v">Делитель</param> /// <param name="r">Остаток</param> /// <returns>Частное</returns> public BigInteger Divide(int v, out int r) { if (v == 0) { throw new Exception("divide by zero"); } BigInteger ans = this.AbsDivide(Math.Abs(v), out r); if ((this.sign && v > 0) || (!this.sign && v < 0)) { return(ans); } ans.ChangeSign(false); r = -r; return(ans); }
/// <summary> /// Сложение с учетом знака /// </summary> /// <param name="value">С чем складываем</param> /// <returns>Результат</returns> public BigInteger Add(BigInteger value) { if (this.sign && value.sign) { return(this.AbsAdd(value)); } if (!this.sign && value.sign) { return(value.AbsSubstract(this)); } if (this.sign && !value.sign) { return(this.AbsSubstract(value)); } BigInteger ans = this.AbsAdd(value); ans.ChangeSign(false); return(ans); }
/// <summary> /// Вычитание /// </summary> /// <param name="value">Вычитаемое</param> /// <returns>Разность</returns> public BigInteger AbsSubstract(BigInteger value) { //Если второе число больше первого if (this.CompareTo(value) == -1) { BigInteger tmp = value.Substract(this); tmp.ChangeSign(false); return(tmp); } //Значение переноса int k = 0; //Максимальная длина числа int n = Math.Max(arr.Count, value.arr.Count); //Результирующий массив List <int> ans = new List <int>(); //Временное значение i-го разряда из первого и второго числа int tmp1, tmp2; for (int i = 0; i < n; i++) { tmp1 = (arr.Count > i) ? arr[i] : 0; tmp2 = (value.arr.Count > i) ? value.arr[i] : 0; ans.Add(tmp1 - tmp2 - k); if (ans[i] < 0) { ans[i] += mybase; k = 1; } else { k = 0; } } return(new BigInteger(Normalize(ans), true)); }
/// <summary> /// Алгоритм Кнута деления длинных чисел /// </summary> /// <param name="q">Частное q[m]..q[0]</param> /// <param name="r">Остаток r[n - 1]..r[0]</param> /// <param name="u">u[m + n - 1]..u[0] — делимое по основанию b</param> /// <param name="v">v[n - 1]..v[0] — делитель по основанию b</param> public static int AbsDivide(out BigInteger q, out BigInteger r, BigInteger u, BigInteger v) { //Начальная инициализация int n = v.arr.Count; int m = u.arr.Count - v.arr.Count; int[] tempArray = new int[m + 1]; tempArray[m] = 1; q = new BigInteger(tempArray, true); /* 1. НОРМАЛИЗАЦИЯ * Нам необходимо преобразовать делимое и делитель, * умножив на коэффициент d. d = [b/(v[n-1]+1)] и умножаем. * Если d=1, то добавляем нулевой разряд u[m+n]=0 */ int d = (mybase / (v.arr[n - 1] + 1)); u = u.Multiply(d); v = v.Multiply(d); //Проверка на d==1 if (u.arr.Count == n + m) { u.arr.Add(0); } //2. Начальная установка j. j = m int j = m; //Цикл по j while (j >= 0) { /*3. Вычислить временное q. tempq=[(u[j+n]*b+u[j+n-1])/v[n-1]], * tempr — остаток от такого деления. Если tempq=b или q*v[n-2]>b*tempr + u[j+n-2], * то tempq = tempq — 1 и tempr = tempr + v[n-1]. Необходимо повторять данную проверку * пока temr < b*/ long cur = (long)(u.arr[j + n]) * (long)(mybase) + u.arr[j + n - 1]; int tempq = (int)(cur / v.arr[n - 1]);//нормализация помогает не выпрыгнуть за границу типа int tempr = (int)(cur % v.arr[n - 1]); do { if (tempq == mybase || (long)tempq * (long)v.arr[n - 2] > (long)mybase * (long)tempr + u.arr[j + n - 2]) { tempq--; tempr += v.arr[n - 1]; } else { break; } }while (tempr < mybase); /*4. Умножить и вычесть. u[j+n]..u[j] = u[j+n]..u[j] — tempq * v[n-1].. v[0]. Значение разрядов должно быть * всегда положительным, поэтому если мы получаем отрицательный разряд, то должны * прибавить b^(n+1). Причем следует запомнить заимствование слева из старшего разряда.*/ BigInteger u2 = new BigInteger(u.arr.GetRange(j, n + 1), true); u2 = u2.Substract(v.Multiply(tempq)); bool flag = false; if (!u2.sign) //если отрицательные { flag = true; List <int> bn = new List <int>(); for (int i = 0; i <= n; i++) { bn.Add(0); } bn.Add(1); u2.ChangeSign(true); u2 = new BigInteger(bn, true).Substract(u2); } /*5.Проверка остатка * q[j] = tempq. Если результат 4 — го шага был отрицательным, то перейти к шагу 6, иначе к шагу 7.*/ q.arr[j] = tempq; if (flag) { /*6.Компенсировать сложение. Вероятность данного шага очень мала, а именно r/b, * т.е при большой базе вы очень редко будете попадать в отрицательные числа. * q[j] = q[j] — 1. u[j+n]..u[j] = u[j+n]..u[j] + v[n-1]..v[0], при этом произойдет * перенос в разряд слева от u[j+n], но им следует пренебречь, так как перенос погашается * заимствованием из того же разряда произведенном на шаге 4.*/ q.arr[j]--; u2 = u2.Add(v); if (u2.arr.Count > n + j) { u2.arr.RemoveAt(n + j); } } //меняем u, так как все вычисления происходят с его разрядами /*7.Цикл по j. j уменьшаем на 1. Если j>=0, то вернуться к шагу 3.*/ for (int h = j; h < j + n; h++) { if (h - j >= u2.arr.Count) { u.arr[h] = 0; } else { u.arr[h] = u2.arr[h - j]; } } j--; } Normalize(q.arr); //8.Денормализация /*q[m]..q[0] — искомое частное, а для получения искомого остатка достаточно u[n-1]..u[0]/d*/ int unusedR = 0; r = new BigInteger(u.arr.GetRange(0, n), true).Divide(d, out unusedR); return(0); }