【问题标题】:How to perform addition of 2 very large (over 50 digits) binary string values in C#如何在 C# 中添加 2 个非常大(超过 50 位)的二进制字符串值
【发布时间】:2021-07-25 03:26:08
【问题描述】:

我一直在考虑添加二进制数,其中二进制数是字符串形式,我们将这两个二进制数相加得到结果二进制数(在字符串中)。

到目前为止我一直在使用这个:

long first = Convert.ToInt64(a, 2);
long second = Convert.ToInt64(b, 2);
long addresult = first + second;
string result = Convert.ToString(addresult, 2);

return result;

感谢 Stackoverflow:Binary addition of 2 values represented as strings

但是,现在我想添加远远大于long 数据类型范围的数字。 例如,一个二进制值,其十进制结果为BigInteger,即非常大的整数,如下所示:

  1. 111101101000010111101000101010001010010010010110011010100001000010110110110000110001101 等于149014059082024308625334669

  2. 1111001101011000001011000111100011101011110100101010010001110101011101010100101000001101000010000110001110100010011101011111111110110101100101110001010101011110001010000010111110011011 等于23307765732196437339985049250988196614799400063289798555

至少我认为是的。

感谢 Stackoverflow:

  1. C# Convert large binary string to decimal system
  2. BigInteger to Hex/Decimal/Octal/Binary strings?

我使用了以上链接中提供的或多或少完美的逻辑。

但是,有没有更简洁的方法来添加给定的两个二进制字符串?

请让我知道,因为这个问题已经在我脑海中萦绕了一段时间。

【问题讨论】:

  • 有什么特别的原因为什么不能只使用二进制加法逻辑来构建一个新字符串,而不是将其转换为某种整数然后再转换回来?
  • @KieranMoynihan 感谢您的提问,其实是有原因的。一个简单的,真的!我不熟悉或根本不知道。当我说我从未使用过任何远程相关的东西时,请相信我。我在作为测试用例之一的评估中遇到了这样的问题。其他测试用例比这更大。

标签: c# biginteger


【解决方案1】:

您可以利用之前使用的相同方案,但使用BigInteger

using System.Linq;
using System.Numerics;

...

BigInteger first = a.Aggregate(BigInteger.Zero, (s, item) => s * 2 + item - '0');
BigInteger second = b.Aggregate(BigInteger.Zero, (s, item) => s * 2 + item - '0');

StringBuilder sb = new StringBuilder();

for (BigInteger addresult = first + second; addresult > 0; addresult /= 2) 
  sb.Append(addresult % 2);

if (sb.Length <= 0)
  sb.Append('0');

string result = string.Concat(sb.ToString().Reverse());

【讨论】:

  • 感谢您的解决方案。我必须说这太小太快了!但我也有同样的问题要问你。这个解决方案是否足以处理比我在这个问题中提供的更大的二进制字符串,比如 30-40 位大?
  • @Abhishek Raj:BigInteger 是一个任意长度的整数值,足够了,可以留出30-40位数字
【解决方案2】:

这个问题很怀旧 - 谢谢。请注意,我的代码具有解释性且效率低下,几乎没有验证,但它适用于您的示例。你绝对不想在现实世界的解决方案中使用这样的东西,这只是为了说明原则上的二进制加法。

BinaryString#1
  111101101000010111101000101010001010010010010110011010100001000010110110110000110001101
  decimal:149014059082024308625334669
BinaryString#2
  1111001101011000001011000111100011101011110100101010010001110101011101010100101000001101000010000110001110100010011101011111111110110101100101110001010101011110001010000010111110011011
  decimal:23307765732196437339985049250988196614799400063289798555
Calculated Sum
  1111001101011000001011000111100011101011110100101010010001110101011101010100101000001101000010001101111011100101011010100101010000000111111000100100101001100110100000111001000100101000
  decimal:23307765732196437339985049251137210673881424371915133224
Check
  23307765732196437339985049251137210673881424371915133224
  decimal:23307765732196437339985049251137210673881424371915133224

这是代码

using System;
using System.Linq;
using System.Numerics;

namespace ConsoleApp3
{
  class Program
  {
    //  return 0 for '0' and 1 for '1' (C# chars promotion to ints)
    static int CharAsInt(char c) { return c - '0'; }

    //  and vice-versa
    static char IntAsChar(int bit) { return (char)('0' + bit); }

    static string BinaryStringAdd(string x, string y)
    {
      //  get rid of spaces
      x = x.Trim();
      y = y.Trim();

      //  check if valid binaries
      if (x.Any(c => c != '0' && c != '1') || y.Any(c => c != '0' && c != '1'))
        throw new ArgumentException("binary representation may contain only '0' and '1'");

      //  align on right-most bit
      if (x.Length < y.Length)
        x = x.PadLeft(y.Length, '0');
      else
        y = y.PadLeft(x.Length, '0');

      //  NNB: the result may require one more bit than the longer of the two input strings (carry at the end), let's not forget this
      var result = new char[x.Length];

      //  add from least significant to most significant (right to left)
      var i = result.Length;
      var carry = '0';
      while (--i >= 0)
      {
        //  to add x[i], y[i] and carry
        //  - if 2 or 3 bits are set then we carry '1' again (otherwise '0')
        //  - if the number of set bits is odd the sum bit is '1' otherwise '0'
        var countSetBits = CharAsInt(x[i]) + CharAsInt(y[i]) + CharAsInt(carry);
        carry = countSetBits > 1 ? '1' : '0';
        result[i] = countSetBits == 1 || countSetBits == 3 ? '1' : '0';
      }

      //  now to make this byte[] a string
      var ret = new string(result);

      //  remember that final carry?
      return carry == '1' ? carry + ret : ret;
    }

    static BigInteger BigIntegerFromBinaryString(string s)
    {
      var biRet = new BigInteger(0);
      foreach (var t in s)
      {
        biRet = biRet << 1;
        if (t == '1')
          biRet += 1;
      }

      return biRet;
    }

    static void Main(string[] args)
    {
      var s1 = "111101101000010111101000101010001010010010010110011010100001000010110110110000110001101";
      var s2 = "1111001101011000001011000111100011101011110100101010010001110101011101010100101000001101000010000110001110100010011101011111111110110101100101110001010101011110001010000010111110011011";
      var sum = BinaryStringAdd(s1, s2);

      var bi1 = BigIntegerFromBinaryString(s1);
      var bi2 = BigIntegerFromBinaryString(s2);

      var bi3 = bi1 + bi2;

      Console.WriteLine($"BinaryString#1\n  {s1}\n  decimal:{bi1}");
      Console.WriteLine($"BinaryString#2\n  {s2}\n  decimal:{bi2}");
      Console.WriteLine($"Calculated Sum\n  {sum}\n  decimal:{BigIntegerFromBinaryString(sum)}");
      Console.WriteLine($"Check\n  {bi3}\n  decimal:{bi3}");

      Console.ReadKey();
    }
  }
}

【讨论】:

  • 感谢您的解决方案。老实说,我仍然在尝试理解用于加法的 while 循环,因为我自己在评估问题时遇到过这个问题,并且在现实世界中从未见过。我必须说您提供的解决方案和 cmets 非常具有解释性。我很肯定它会帮助我更多地理解这一点。我只有一个问题。如果测试用例比提供的示例更小(在 int 数据类型范围内)以及更​​大的示例,是否可以使用此解决方案??
  • 它适用于任意长度的字符串。但是,如果您打算来回转换为系统整数类型,请注意这些字符串是从最高有效位到最低有效位“直观地排序”的(正如您在教科书中所期望的那样),但在英特尔平台上,数字类型以小端顺序存储(反转,从最低有效字节到最高有效字节)。值为 46971 (0xB77B) 的 UInt16 将以字节顺序出现在内存中 |7B|B7|、0x12345678 为 |78|56|34|12| 等。您还需要注意有符号类型(2s 补码表示法)。祝您旅途愉快。
【解决方案3】:

我将在 AlanK 旁边添加一个替代解决方案,作为一个示例,说明您如何在不将数字转换为某种形式的整数之前进行此操作。

        static string BinaryStringAdd(string b1, string b2)
        {
            char[] c = new char[Math.Max(b1.Length, b2.Length) + 1];

            int carry = 0;
            for (int i = 1; i <= c.Length; i++)
            {
                int d1 = i <= b1.Length ? b1[^i] : 48;
                int d2 = i <= b2.Length ? b2[^i] : 48;

                int sum = carry + (d1-48) + (d2-48);

                if (sum == 3)
                {
                    sum = 1;
                    carry = 1;
                }
                else if (sum == 2)
                {
                    sum = 0;
                    carry = 1;
                }
                else
                {
                    carry = 0;
                }

                c[^i] = (char) (sum+48);
            }

            return c[0] == '0' ? String.Join("", c)[1..] : String.Join("", c);
        }

请注意,此解决方案比 Alan 的解决方案慢约 10%(至少对于此测试用例而言),并假设字符串到达​​格式正确的方法。

【讨论】:

  • 根据添加后大数的用途,这似乎是一个更有效的解决方案。
  • @kierenMoynihan 感谢您的解决方案。我必须承认,这个解决方案看起来比上面的解决方案更简单、更时尚。只是为了确认一下,这可以用于比提供的更大的文本案例吗?
  • @AbhishekRaj 是的。只要您可以在您身边填充字符串变量(即适合字符串的值),它就应该能够运行。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-20
  • 1970-01-01
  • 1970-01-01
  • 2020-02-22
  • 2021-05-19
相关资源
最近更新 更多