Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 8 years ago.
Improve this question
I have written some code to multiply really long numbers. Was wondering if there are more efficient ways to do this?
Here's how I've done it for now. Basically implemented the typical 'Long multiplication' technique.
internal enum Digit
{
Zero = 0,
One,
Two,
Three,
Four,
Five,
Six,
Seven,
Eight,
Nine
}
public class NumbersWhiz
{
public string Add(string Augend, string Addend)
{
string longerNum = (Augend.Length > Addend.Length == true) ? Augend : Addend;
string shorterNum = (Addend.Length < Augend.Length == true) ? Addend : Augend;
int longerLen = (Augend.Length > Addend.Length == true) ? Augend.Length : Addend.Length;
int shorterLen = (Addend.Length < Augend.Length == true) ? Addend.Length : Augend.Length;
//Pad the shorter number initially with zeros to match length of longer number
int deltaLen = longerLen - shorterLen;
string numTwoZeroed = new String('0', deltaLen);
string numTwo = numTwoZeroed.Insert(deltaLen, shorterNum);
string numOne = longerNum;
string result = new String('0', longerLen);
StringBuilder resultBuilder = new StringBuilder(result);
bool carryForward = false;
for (int index = longerLen; index > 0; index--)
{
int augend = Convert.ToInt32(numOne.Substring(index - 1, 1));
int addend = Convert.ToInt32(numTwo.Substring(index - 1, 1));
int sum = (carryForward == true) ? 1 : 0;
sum = sum + augend + addend;
carryForward = ((sum > 9) == true) ? true : false;
int reminder = sum % 10;
resultBuilder[index - 1] = Convert.ToChar(reminder.ToString());
}
if(carryForward)
resultBuilder.Insert(0, '1');
return resultBuilder.ToString();
}
public string Multiply(string Multiplicand, string Multiplier)
{
int resultLen = Multiplicand.Length + Multiplier.Length;
string totalSum = new String('0', resultLen);
for (int index = Multiplier.Length; index > 0; index--)
{
int multiplierDigit = Convert.ToInt32(Multiplier.Substring(index - 1, 1));
string product = Multiply(Multiplicand, (Digit)multiplierDigit);
product += new String('0', Multiplier.Length - index);
totalSum = Add(totalSum, product);
}
return totalSum;
}
string Multiply(string Multiplicand, Digit MultiplierDigit)
{
int multiplier = (int)MultiplierDigit;
if (multiplier == 0)
return "0";
int carry = 0;
bool carryForward = false;
int len = Multiplicand.Length;
int productLen = len + 1;
string result = new String('0', productLen);
StringBuilder resultBuilder = new StringBuilder(result);
for (int index = len; index > 0; index--)
{
int multiplicandDigit = Convert.ToInt32(Multiplicand.Substring(index - 1, 1));
int product = (multiplicandDigit * multiplier) + carry;
carryForward = ((product > 9) == true) ? true : false;
int reminder = product % 10;
carry = (product - reminder) / 10;
resultBuilder[index] = Convert.ToChar(reminder.ToString());
}
if (carryForward)
resultBuilder[0] = Convert.ToChar(carry.ToString());
return resultBuilder.ToString();
}
}
Yes--this is a digit-by-digit operation.
You have a couple of obvious options for doing things faster. One is a binary operation, where you treat one of the numbers as the sum of powers of two, and the result also as the sum of the partial results you get by multiplying by those powers of two.
For example, let's do 17 x 11 (which should give us 181, I believe).
So, let's think of 17 as powers of 2. It's 20 + 24 (i.e., 1 + 16). So we can take 11 * 1 + 11 * 16. We can do each of these multiplications with a shift, so it's 11<<0 + 11<<4.
Another way to look at things (that leads to a somewhat different way of doing things) is useful for large numbers. For the sake of argument, let's assume you can only do 4-bit operations. In this case, you can think of each number in 4-bit pieces and use the distributive property of multiplication to get a result--that is, we take each large number, and break it up into the sum of numbers, each of which represents a "slice" of the bits that make up the whole number. For example, consider something like 0x1234 * 0x4321, and (for the same of simplicity) we'll assume we're going to multiply them with a CPU that can multiply two 8-bit operands to produce a 16-bit result. So, we break each of those up into 8-bit slices:
(0x1200 + 0x34) * (0x4300 + 0x21)
Then we can use the distributive property:
0x1200 * 0x4300 + 0x1200 * 0x21 + 0x34 * 0x4300 + 0x34 * 0x21
Each of these (obviously enough) has only 8 significant bits, so we can carry out each of the operations on our 8-bit CPU. Then you basically just have to take the 4 intermediate results and add them all together. Any reasonable CPU will have a carry bit and an add-with-carry instruction you can use to handle this multiple precision operation.
Although I've shown it with 8-bit operations here, I think it's pretty obvious how this extends to (for example) 256-bit operands on a 32-bit or 64-bit CPU.
Well, yes. There are more advanced multiplication methods.
A quick and easy way to speed up your algorithm a bit is to move from base-10 (aka decimal places) into a number system which is more appropriate for computers. working with 32 bit or 64 bit integers in base-2 will be much faster. You do more work per calculation and also get rid of all the modulo calculations.
Beyond that you could replace the (trivial) multiplication algorithm by something better. If your numbers start to get really large you can get huge speedups by moving into a different complexity region. Your algorithm has complexity O(n*m) where n and m are the number of digits of the two factors.
The Fast Fourier Transform can be used to do huge number multiplications much faster in O(n log n). Worth mentioning is the Number Theoretic Transform which is even more suited for this task.
There is a lot to learn and explore in the topic of large integer arithmetic. If you however just want to multiply numbers and don't care about how it's done I suggest to just use a tested and fast bignum library.
Related
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 2 years ago.
Improve this question
For a given input n, the task is to find the largest integer that is <= n and has the highest digit sum.
For example:
solve(100) = 99. Digit Sum for 99 = 9 + 9 = 18. No other number <= 100 has a higher digit sum.
solve(10) = 9
solve(48) = 48. Note that 39 is also an option, but 48 is larger.
Input range is 0 < n < 1e11
What have I tried?
I tried 2 methods. Firstly, I tried getting each digit with Math operations like this:
public static long solve(long n)
{
var answer = 0;
var highestSum = 0;
for (var i = 1; i <= n; i++)
{
var temp = i;
var sum = 0;
while (temp > 0)
{
sum += temp % 10;
temp /= 10;
}
if (sum >= highestSum)
{
highestSum = sum;
answer = i;
}
}
return answer;
}
My second try, I tried using Linq extensions, like this:
public static long solve(long n)
{
var answer = 0;
var highestSum = 0;
for (var i = 1; i <= n; i++)
{
var sum = i.ToString().Sum(x => x - '0');
if (sum >= highestSum)
{
highestSum = sum;
answer = i;
}
}
return answer;
}
Both of my solutions seem to return the correct value and work for smaller values, but for larger input, they seem to take a very long time to execute. How to make it run through numbers faster? Is there a specific algorithm for this task, or am I doing something else wrong?
We can achieve this O(number of digits in n)
We can achieve this if we iteratively reduce a digit and change all other digits on its right to 9.
Let n be our current number.
We can find next number using the below :
b is a power of 10 to represent position of current digit. After every iteration we reduce n to n/10 and change b to b*10.
We use (n – 1) * b + (b – 1);
For eg, if the number is n = 521 and b = 1, then
(521 – 1) * 1 + (1-1) which gives you 520, which is the thing we need to do, reduce the position number by 1 and replace all other numbers to the right by 9.
After n /= 10 gives you n as 52 and b*=10 gives you b as 10, which is again executed as (52-1)*(10) + 9 which gives you 519, which is what we have to do, reduce the current index by 1 and increase all other rights by 9.
static int findMax(int x)
{
int b = 1, ans = x;
while (x!=0)
{
int cur = (x - 1) * b + (b - 1);
if (sumOfDigits(cur) >= sumOfDigits(ans) && cur > ans))
ans = cur;
x /= 10;
b *= 10;
}
return ans;
}
int sumOfDigits(int a)
{
int sum = 0;
while (a)
{
sum += a % 10;
a /= 10;
}
return sum;
}
The accepted answer is brilliant, but I was dead-set on figuring out a way to determine the correct answer without actually summing the digits and comparing the sums to each other.
I tried a few things (as you can see if you look at the edit history), but I couldn't find the formula. In desperation, I wrote a utility to show me all the numbers from 1 to 9999999 that did not have a smaller number with a larger sum to see what pattern I was missing by not looking on a large enough scale.
I was somewhat surprised that only 253 numbers out of the first 10 million have the largest sum compared to their lessers! Somehow I thought that number would be bigger.
Also, it turns out that there is an obvious pattern that appears fairly quickly, and it remained constant for 10 million iterations, so I think it's a good one.
Here's a small sample of some blocks of consecutive output:
0,1,2,3,4,5,6,7,8,9,
18,19,28,29,38,39,48,49,
58,59,68,69,78,79,88,89,98,99,189,198
8899,8989,8998,8999,
9899,9989,9998,9999,
18999,19899,19989,19998,19999
98999,99899,99989,99998,99999,
189999,198999,199899,199989,199998,199999
7899999,7989999,7998999,7999899,7999989,7999998,7999999,
8899999,8989999,8998999,8999899,8999989,8999998,8999999,
9899999,9989999,9998999,9999899,9999989,9999998,9999999
It's so obviously clear!
If the number is one digit, then it's the highest.
If all but the first digit are either all 9's or all 9's with a single 8, then it's sum is the highest.
Otherwise the highest number is the one whose first digit is one less than the original, followed by all 9's.
Here's a code implementation:
public static long Solve(long n)
{
if (HasValidSuffix(n)) return n;
long firstDigit;
int numDigits;
// Loop to determine the first digit and number of digits in the input
for (firstDigit = n, numDigits = 1; firstDigit > 9; firstDigit /= 10, numDigits++) ;
return Enumerable.Range(0, numDigits - 1)
.Aggregate(firstDigit - 1, (accumulator, next) => accumulator * 10 + 9);
}
// Returns true for positive numbers less than 10 or
// numbers that end in either all 9's or all 9's and one 8
public static bool HasValidSuffix(long input)
{
var foundAnEight = false;
for (var n = input; n > 9; n /= 10)
{
var lastDigit = n % 10;
if (lastDigit < 8) return false;
if (lastDigit == 9) continue;
if (foundAnEight) return false;
foundAnEight = true;
}
return true;
}
I have a task to find pairs of amicable numbers and I've already solved it. My solution is not efficient, so please help me to make my algorithm faster.
Amicable numbers are two different numbers so related that the sum of the proper divisors of each is equal to the other number. The smallest pair of amicable numbers is (220, 284). They are amicable because the proper divisors of 220 are 1, 2, 4, 5, 10, 11, 20, 22, 44, 55 and 110, of which the sum is 284; and the proper divisors of 284 are 1, 2, 4, 71 and 142, of which the sum is 220.
Task: two long numbers and find the first amicable numbers between them. Let s(n) be the sum of the proper divisors of n:
For example:
s(10) = 1 + 2 + 5 = 8
s(11) = 1
s(12) = 1 + 2 + 3 + 4 + 6 = 16
If s(firstlong) == s(secondLong) they are amicable numbers
My code:
public static IEnumerable<long> Ranger(long length) {
for (long i = 1; i <= length; i++) {
yield return i;
}
}
public static IEnumerable<long> GetDivisors(long num) {
return from a in Ranger(num/2)
where num % a == 0
select a;
}
public static string FindAmicable(long start, long limit) {
long numberN = 0;
long numberM = 0;
for (long n = start; n <= limit; n++) {
long sumN = GetDivisors(n).Sum();
long m = sumN;
long sumM = GetDivisors(m).Sum();
if (n == sumM ) {
numberN = n;
numberM = m;
break;
}
}
return $"First amicable numbers: {numberN} and {numberM}";
}
I generally don't write C#, so rather than stumble through some incoherent C# spaghetti, I'll describe an improvement in C#-madeup-psuedo-code.
The problem seems to be in your GetDivisors function. This is linear O(n) time with respect to each divisor n, when it could be O(sqrt(n)). The trick is to only divide up to the square root, and infer the rest of the factors from that.
GetDivisors(num) {
// same as before, but up to sqrt(num), plus a bit for floating point error
yield return a in Ranger((long)sqrt(num + 0.5)) where num % a == 0
if ((long)sqrt(num + 0.5) ** 2 == num) { // perfect square, exists
num -= 1 // don't count it twice
}
// repeat, but return the "other half"- num / a instead of a
yield return num/a in Ranger((long)sqrt(num + 0.5)) where num % a == 0
}
This will reduce your complexity of that portion from O(n) to O(sqrt(n)), which should provide a noticeable speedup.
There is a simple formula giving the sum of divisors of a number knowing its prime decomposition:
let x = p1^a1 * ... * pn^an, where pi is a prime for all i
sum of divisors = (1+p1+...+p1^a1) * ... (1+pn+...+pn^an)
= (1-p1^(a1+1))/(1-p1) * ... ((1-pn^(an+1))/(1-pn)
In order to do a prime decomposition you must compute all prime numbers up to the square root of the maximal value in your search range. This is easily done using the sieve of Erathostenes.
I ran into this question when watching the twit.tv show coding 101 (episode 2). The code was pre written, but we were asked to change it in order to print out all the values of the while loop that converted an integer to a binary number.
I was able to print out everything with a simple "Console.WriteLine(number);" line. When doing so, it returns all the values for the 32-bit int 0's included.
My question, is there a way to trim or remove all the extra 0's in the division of the loop (not in the final binary number) so they are not printed? Here is the code of the program:
public static string ConvertIntToBinary(int number)
{
int bits = (sizeof(int) * 16); //32bits
char[] result = new char[bits]; //Array to hold the binary numbers http://msdn.microsoft.com/en-us/library/aa288453(v=vs.71).aspx
while (bits > 0)
{
bits = bits - 1;
int remainder = number % 2; //% called mod or modulo which computes the remainder after dividing http://msdn.microsoft.com/en-us/library/0w4e0fzs.aspx
if (remainder == 1) //If remainder is 1, store it as 1
result[bits] = '1';
else
result[bits] = '0'; //Otherwise store it as 0
number = number / 2; //Take the original number, divide it by 2
Console.WriteLine(number);
}
return new string(result).TrimStart('0'); //return the result as a string ,removing extra 0's
}
Terminate the loop when number reaches 0. At that point there are no more non-zero bits to pull off:
public static string ConvertIntToBinary(uint value)
{
int totalbits = sizeof(int) * 8;
char[] result = new char[totalbits];
int bits = totalbits;
uint number = value;
while (bits > 0)
{
bits--;
uint remainder = number % 2;
result[bits] = remainder == 0 ? '0' : '1';
number /= 2;
if (number == 0)
break;
}
return new string(result, bits, totalbits - bits);
}
I fixed an error in the code: there are 8 bits in a byte, and not 16 as per your code. I also simplified a few parts of the code. I used a conditional expression which is more concise than the if. And I introduced another local variable to hold the working value to avoid modifying the actual parameter. This is generally good practise that makes debugging easier. I also used uint for the input parameter since the entire approach depends on the value being positive.
Note that the termination is inside the loop rather than in the while test. If you test for number equal to 0 in the while condition then you will end up returning the empty string for an input of 0.
Just ignore all the Zero bits as you loop until you hit a '1' bit.
This works with negative numbers as well.
public static string ConvertIntToBinary(int number)
{
if (number == 0) return "0";
var bits = (sizeof(int) * 8); // 8bits per byte
var sb = new StringBuilder();
var print = false;
var mask = (uint)(1 << bits - 1);
while (bits-->0)
{
var bit = (number & mask) == mask;
if (bit) print = true;
if (print) sb.Append(bit ? '1' : '0');
mask = mask >> 1;
}
return sb.ToString();
}
I have very long 5 strings (the number of strings may change).There is no fixed format for these strings. I will provide a number which will indicate the length of the substring. I want to find the matching substrings with the given length. For example the strings are:
1. abcabcabc
2. abcasdfklop
string length: 3
Given these values the output will be something like this:
Match #1:
Matched string : "abc"
Matches in first string: 3
Matching positions: 0,3,6
Matches in second string: 1
Match positions: 0
Match #2:
Matched string : "bca"
Matches in first string: 2
Matching positions: 1,4
Matches in second string: 1
Match positions: 1
I managed to do it in 4 foreach statement. But it seemed to me too unefficient. Especially if the input sizes are very big.Is there any suggestion or short way to manage this more efficient in c#?
You can do this with a suffix array. (Suffix trees will work fine too, but they require a bit more space, time, and care in implementation.)
Concatenate your two strings, separating them with a character that occurs in neither one. Then build a suffix array. Then you can read off your answer.
Standard suffix arrays give you a lexicographically sorted array of pointers to suffixes of the string together with a "longest common prefix length" array telling you how long the longest common prefix of two lexicographically consecutive suffixes is.
It is fairly straightforward to use the longest common prefix length array to get the information you want; find all maximal subarrays of the longest common prefix length array for which the longest common prefix length is at least the query length, then, for each one that has a match both in the first string and in the second string, report the appropriate prefix and report that it occurs K+1 times, where K is the length of the maximal subarray.
Another approach that's easier to code is to hash all substrings of the appropriate length. You can do this easily with any rolling hash function. Store a dynamic array of pointers into the strings for each hash; once you've hashed all the strings, iterate over all of the hashes that came up and look for matches. You'll need to deal with the false positives somehow; one (probabilistic) approach is to use several hash functions until the false positive probability is acceptably small. Another approach, which is likely only acceptable in the case where you have few matches, is to compare the strings directly.
If you managed to do this in 4 foreach statements that are not nested then you should be good and you probably don’t need to optimize.
Here is something I’d try.
Create a structure that looks something like this
class SubString
{
string str;
int position;
}
Divide both strings into all possible substrings and store these into one array. This has a O(n2) complexity.
Now sort these arrays by string length ( O(n*log(n)) complexity) and go through both of these to identify matches.
You’ll need additional structure to hold the results and this probably needs some more tweaking but you see where this is going.
You could use a variant of suffix tree to solve this problem. http://en.wikipedia.org/wiki/Longest_common_substring_problem
Also check this out: Algorithm: Find all common substrings between two strings where order is preserved
If using very large strings, memory may become a problem. The code below finds the longest common substring and writes over the variable containing smaller common substrings, but could easily be altered to push the index and length to a list which is then returned as an array of strings.
This is refactored C++ code from Ashutosh Singh at https://iq.opengenus.org/longest-common-substring-using-rolling-hash/ - this will find the substring in O(N * log(N)^2) time and O(N) space
using System;
using System.Collections.Generic;
public class RollingHash
{
private class RollingHashPowers
{
// _mod = prime modulus of polynomial hashing
// any prime number over a billion should suffice
internal const int _mod = (int)1e9 + 123;
// _hashBase = base (point of hashing)
// this should be a prime number larger than the number of characters used
// in my use case I am only interested in ASCII (256) characters
// for strings in languages using non-latin characters, this should be much larger
internal const long _hashBase = 257;
// _pow1 = powers of base modulo mod
internal readonly List<int> _pow1 = new List<int> { 1 };
// _pow2 = powers of base modulo 2^64
internal readonly List<long> _pow2 = new List<long> { 1L };
internal void EnsureLength(int length)
{
if (_pow1.Capacity < length)
{
_pow1.Capacity = _pow2.Capacity = length;
}
for (int currentIndx = _pow1.Count - 1; currentIndx < length; ++currentIndx)
{
_pow1.Add((int)(_pow1[currentIndx] * _hashBase % _mod));
_pow2.Add(_pow2[currentIndx] * _hashBase);
}
}
}
private class RollingHashedString
{
readonly RollingHashPowers _pows;
readonly int[] _pref1; // Hash on prefix modulo mod
readonly long[] _pref2; // Hash on prefix modulo 2^64
// Constructor from string:
internal RollingHashedString(RollingHashPowers pows, string s, bool caseInsensitive = false)
{
_pows = pows;
_pref1 = new int[s.Length + 1];
_pref2 = new long[s.Length + 1];
const long capAVal = 'A';
const long capZVal = 'Z';
const long aADif = 'a' - 'A';
unsafe
{
fixed (char* c = s)
{
// Fill arrays with polynomial hashes on prefix
for (int i = 0; i < s.Length; ++i)
{
long v = c[i];
if (caseInsensitive && capAVal <= v && v <= capZVal)
{
v += aADif;
}
_pref1[i + 1] = (int)((_pref1[i] + v * _pows._pow1[i]) % RollingHashPowers._mod);
_pref2[i + 1] = _pref2[i] + v * _pows._pow2[i];
}
}
}
}
// Rollingnomial hash of subsequence [pos, pos+len)
// If mxPow != 0, value automatically multiply on base in needed power.
// Finally base ^ mxPow
internal Tuple<int, long> Apply(int pos, int len, int mxPow = 0)
{
int hash1 = _pref1[pos + len] - _pref1[pos];
long hash2 = _pref2[pos + len] - _pref2[pos];
if (hash1 < 0)
{
hash1 += RollingHashPowers._mod;
}
if (mxPow != 0)
{
hash1 = (int)((long)hash1 * _pows._pow1[mxPow - (pos + len - 1)] % RollingHashPowers._mod);
hash2 *= _pows._pow2[mxPow - (pos + len - 1)];
}
return Tuple.Create(hash1, hash2);
}
}
private readonly RollingHashPowers _rhp;
public RollingHash(int longestLength = 0)
{
_rhp = new RollingHashPowers();
if (longestLength > 0)
{
_rhp.EnsureLength(longestLength);
}
}
public string FindCommonSubstring(string a, string b, bool caseInsensitive = false)
{
// Calculate max neede power of base:
int mxPow = Math.Max(a.Length, b.Length);
_rhp.EnsureLength(mxPow);
// Create hashing objects from strings:
RollingHashedString hash_a = new RollingHashedString(_rhp, a, caseInsensitive);
RollingHashedString hash_b = new RollingHashedString(_rhp, b, caseInsensitive);
// Binary search by length of same subsequence:
int pos = -1;
int low = 0;
int minLen = Math.Min(a.Length, b.Length);
int high = minLen + 1;
var tupleCompare = Comparer<Tuple<int, long>>.Default;
while (high - low > 1)
{
int mid = (low + high) / 2;
List<Tuple<int, long>> hashes = new List<Tuple<int, long>>(a.Length - mid + 1);
for (int i = 0; i + mid <= a.Length; ++i)
{
hashes.Add(hash_a.Apply(i, mid, mxPow));
}
hashes.Sort(tupleCompare);
int p = -1;
for (int i = 0; i + mid <= b.Length; ++i)
{
if (hashes.BinarySearch(hash_b.Apply(i, mid, mxPow), tupleCompare) >= 0)
{
p = i;
break;
}
}
if (p >= 0)
{
low = mid;
pos = p;
}
else
{
high = mid;
}
}
// Output answer:
return pos >= 0
? b.Substring(pos, low)
: string.Empty;
}
}
I have read many fine algorithms for identifying the most significant bit for 32- and 64-bit integers (including other posts here on SO). But I am using BigIntegers, and will be dealing with numbers up to 4000 bits long. (The BigInteger will hold the Hilbert index into the Hilbert space-filling curve that meanders through a 1000-dimension hypercube at a fractal depth of 4.) But the bulk of the cases will involve numbers that could fit inside a 64 bit integer, so I want a solution that is optimal for the common cases but can handle the extreme cases.
The naive way is:
BigInteger n = 234762348763498247634;
int count = 0;
while (n > 0) {
n >>= 1;
count++;
}
I was thinking of converting common cases to Longs and using a 64-bit algorithm on those, otherwise using a different algorithm for the really big numbers. But I am not sure how expensive the conversion to a Long is, and whether that will swamp the efficiencies of doing the remainder of the computation on a 64-bit quantity. Any thoughts?
One intended use for this function is to help optimize inverse gray code calculations.
Update. I coded two approaches and ran a benchmark.
If the number was under Ulong.MaxValue, then converting to a Ulong and doing the binary search approach was twice as fast as using BigInteger.Log.
If the number was very large (I went as high as 10000 bits), then Log was 3.5 times faster.
96 msec elapsed for one million calls to MostSignificantBitUsingLog
(convertable to Long).
42 msec elapsed for one million calls to
MostSignificantBitUsingBinarySearch (convertable to Long).
74 msec elapsed for ten thousand calls to MostSignificantBitUsingLog
(too big to convert).
267 msec elapsed for ten thousand calls to
MostSignificantBitUsingBinarySearch (too big to convert).
Here is the code for using Log:
public static int MostSignificantBitUsingLog(BigInteger i)
{
int bit;
if (i == 0)
bit = -1;
else
bit = (int)BigInteger.Log(i, 2.0);
return bit;
}
Here is my approach to binary search. It could be improved to extend the binary division up into the BigInteger range. I will try that next.
public static int MostSignificantBitUsingBinarySearch(BigInteger i)
{
int bit;
if (i.IsZero)
bit = -1;
else if (i < ulong.MaxValue)
{
ulong y = (ulong)i;
ulong s;
bit = 0;
s = y >> 32;
if (s != 0)
{
bit = 32;
y = s;
}
s = y >> 16;
if (s != 0)
{
bit += 16;
y = s;
}
s = y >> 8;
if (s != 0)
{
bit += 8;
y = s;
}
s = y >> 4;
if (s != 0)
{
bit += 4;
y = s;
}
s = y >> 2;
if (s != 0)
{
bit += 2;
y = s;
}
s = y >> 1;
if (s != 0)
bit++;
}
else
return 64 + MostSignificantBitUsingBinarySearch(i >> 64);
return bit;
}
Update 2: I changed my binary search algorithm to work against BigIntegers up to one million binary digits and not call itself recursively in 64 bit chunks. Much better. Now it takes 18 msec to run my test, and is four times faster than calling Log! (In the code below, MSB is my ulong function that does the same sort of thing, with the loop unrolled.)
public static int MostSignificantBitUsingBinarySearch(BigInteger i)
{
int bit;
if (i.IsZero)
bit = -1;
else if (i < ulong.MaxValue)
bit = MSB((ulong)i);
else
{
bit = 0;
int shift = 1 << 20; // Accommodate up to One million bits.
BigInteger remainder;
while (shift > 0)
{
remainder = i >> shift;
if (remainder != 0)
{
bit += shift;
i = remainder;
}
shift >>= 1;
}
}
return bit;
}
You can calculate the log2 which represent the number of bits needed:
var numBits = (int)Math.Ceil(bigInt.Log(2));
You can treat it like a binary-search problem.
You have an upper limit of 4000 (add some room maybe)
int m = (lo + hi) / 2;
BigInteger x = BigInteger(1) << m;
if (x > n) ...
else ...
In .Net 5 this is now built-in...
int log2 = myBigInt.GetBitLength()
If you can use Java rather than C#, there is a library for arbitrary precision Hilbert curve indexing that you can find at http://uzaygezen.googlecode.com. For the implementation of the gray code inverse, you may want to have a closer look at LongArrayBitVector.grayCodeInverse or perhaps BitSetBackedVector.grayCodeInverse in the mentioned project.
8 years late, to find the MSB top bit (aka Log2) I came up with this quick method...
static int GetTopBit(BigInteger value)
{
if (value < 0)
BigInteger.Negate(value);
int lowerBytes = value.GetByteCount(true) - 1;
int t = value.ToByteArray(true)[lowerBytes];
int top = t > 127 ? 8 : t > 63 ? 7 : t > 31 ? 6 : t > 15 ? 5 : t > 7 ? 4 : t > 3 ? 3 : t > 1 ? 2 : 1;
int topbit = (top + lowerBytes * 8);
return topbit;
}