I am using a generic class to convert an INT to a X base:
BaseX basex = new BaseX("abcdefghijklmnopqrstuvwxyz");
var a = basex.ToBaseX(1002);
var b = basex.FromBaseX("aghe");
And the BaseX class is as follows:
public class BaseX {
private readonly string _digits;
public BaseX(string digits) {
_digits = digits;
}
public string ToBaseX(int number) {
var output = "";
do {
output = _digits[number % _digits.Length] + output;
number = number / _digits.Length;
}
while (number > 0);
return output;
}
public int FromBaseX(string number) {
return number.Aggregate(0, (a, c) => a * _digits.Length + _digits.IndexOf(c));
}
}
I am using the lowercase base but I can use any other base.
Is it possible to make the output in the base X always the same length?
I think I should use "Multiplicative Inverse" and some similar process with mapping and encoding but I am not sure how to do this ...
Could I get some help to create this?
Basically, my objective is instead of creating random fixed lenght codes to use in promotions or in ID obfuscation just create a fixed length of an INT (The ID on the database).
Thank You,
Miguel
If I understand you correctly you want to pad the generated value with "zeroes". E.g. if you were using plain numbers and you wanted an ID of length 10 and the ID was 1234 the padded ID would be 0000001234.
The simplest way is to pad the generated value. You can add a new method to the BaseX class:
public string ToBaseX(int number, int width) {
var output = ToBaseX(number);
return output.PadLeft(width, _digits[0]);
}
With this method basex.ToBaseX(1002, 10) returns
aaaaaaabmo
and basex.FromBaseX("aaaaaaabmo") returns
1002
In the comments you indicate that the resulting string aaaaaaabmo does not seem very random. But then you can use the approach that Eric Lippert describes in the article A practical use of multiplicative inverses that you are referring to.
First you need to pick an upper limit to the numbers you want to obfuscate (and this number should fit into a 32 bit integer). Eric Lippert uses 1000000000 (1 billion). You then need to pick a number less than the limit that is coprime with the limit (e.g. they do not share any prime factors). Eric Lippert chooses 387420489 (and explains that any number that ends in 9 will be coprime with a number that is a power of 10). You then need to calculate the modular multiplicative inverse of this number, e.g. a number inverse-x that satisfies the following condition:
387420489 * inverse-x = 1 (mod 1000000000)
You can use the extended Euclidian algorithm for this calculation for instance using an online calculator. The modular multiplicative inverse is 513180409.
To obfuscate you number you can use this code (to avoid overflow it is important to perform the calculation using 64 bit integers):
var value = 1002;
var m = 1000000000L;
var x = 387420489L;
var inverseX = 513180409L;
var encoded = value*x%m;
var decoded = encoded*inverseX%m;
For this particular calculation encoded is 195329978.
If you want to use the lower case letters to represent the obfuscated number you can use your BaseX class to convert the number to base 26. You can compute the maximum letters required to represent any number below 1 billion:
Math.Log(1000000000)/Math.Log(26) = 6.36054383137796
This means that you need no more than 7 letters to represent your number.
I have combined all this into two simple methods using some constants you can easily customize:
static class Obfuscator {
const Int64 modulo = 1000000000L;
const Int64 coprime = 280619659L;
const Int64 inverseCoprime = 687208739L;
const String digits = "abcdefghijklmnopqrstuvwxyz";
const Int32 maxDigits = 7; // Math.Log(modulo)/Math.Log(digits.Length) rounded up.
public static String Obfuscate(Int32 originalValue) {
if (originalValue >= modulo || originalValue < 0)
throw new ArgumentOutOfRangeException();
var value = (Int32) (originalValue*coprime%modulo);
var buffer = new Char[maxDigits];
var i = maxDigits;
do {
buffer[--i] = digits[value%digits.Length];
value /= digits.Length;
} while (value > 0);
while (i > 0)
buffer[--i] = digits[0];
return new String(buffer);
}
public static Int32 Deobfuscate(String obfuscatedValue) {
if (String.IsNullOrEmpty(obfuscatedValue))
throw new ArgumentException();
var value = obfuscatedValue
.Aggregate(0, (a, c) => a*digits.Length + digits.IndexOf(c));
return (Int32) (value*inverseCoprime%modulo);
}
}
Only detail to be aware of is that 0 is obfuscated into aaaaaaa. For any number between 1 and 999999999 (inclusive) you get what looks like a random string of 7 characters.
Related
I have this code which I think I found somewhere on the internet some years ago and it doesn't quite work.
The purpose is to take any string and from that create a string that is lexicographically sorted by a large number - because then inverse (descending) ordering can be achieved by subtracting the number from another even larger number.
private static BigInteger maxSort = new BigInteger(Encoding.Unicode.GetBytes("5335522543087813528200259404529154678271640415603227881439560533607051111046319775598721171814499900"));
public static string GetSortString(string str, bool descending)
{
var sortNumber = new BigInteger(Encoding.Unicode.GetBytes(str));
if (descending)
{
sortNumber = maxSort - sortNumber;
}
return "$SORT!" + sortNumber.ToString().PadLeft(100, '0') + ":" + str;
}
The reason I need this is because I want to use it to insert as RowKey in Azure Table Storage which is the only way to sort in Table Storage. I need to sort any text, any number and any date, both ascending and descending.
Can anyone see the issue with the code or have any code that serves the same purpose?
The question is tagged with C# but of course this is not a question of syntax so if you have the answer in any other code that would be fine too.
Example
I want to convert any string to a number which is lexicographically sorted correctly - because if it's a number, then I can invert it and sort descending.
So for example, if I can convert:
ABBA to 1234
Beatles to 3131
ZZ Top to 9584
Then those numbers would sort them correctly ... and, if I subtract them from a large number, I would be able to invert the sort order:
10000 - 1234 = 8766
10000 - 3131 = 6869
10000 - 9584 = 0416
Of course, to support longer text input, I need to subtract them from a very large number, which is why I use the very large BigInteger.
Current output from this code
ABBA: $SORT!0000000000000000000000018296156958359617:ABBA
Beatles: $SORT!0000000009111360792640460912278748069954:Beatles
ZZ TOP: $SORT!0000000000000096715522885596192519618650:ZZ TOP
As you can see, the longest text gets the highest number. I have also tried to add padding immediately on the input str, but that didnt help either.
Answer
The accepted answer worked. For descending sort order, the "BigInteger" trick from above could be used.
There is some limitation as to how long the sortable string can be.
Here is the final code:
private static BigInteger maxSort = new BigInteger(Encoding.Unicode.GetBytes("5335522543087813528200259404529154678271640415603227881439560533607051111046319775598721171814499900"));
public static string GetSortString(string str, bool descending)
{
BigInteger result = 0;
int maxLength = 42;
foreach (var c in str.ToCharArray())
{
result = result * 256 + c;
}
for (int i = str.Length; i < maxLength; i++)
{
result = result * 256;
}
if (descending)
{
result = maxSort - result;
}
return "$SORT!" + result;
}
If you were looking for a way to give a a value to any string so that you could sort them accordingly to the number and get the same result as above you can't. The reason is that strings don't have any length limit. Because you can always add a char to a string and thereby get a larger number even through it should have a lower lexicographical value.
If they have a length limit you can do something like this
pseudo code
bignum res = 0;
maxLength = 42;
for (char c : string)
res = res * 256 + c
for (int i = string.length; i < maxLength; i++)
res = res *256
If you want to optimize a bit, the last loop could be a lookup table. If your only using a-z, the times 256 could reduced to 26 or 32.
I need to write values like:
9.6 x 10²
9.6 x 10¹²
I need to know if there is a way to format numbers as above in a string.
You have to find the appropriate character from the code page you are using, for example UTF-8:
string superScript2 = "²";
There is no such thing as formatting in a string, it is just all data.
Try this:
public static string Eng(this double x, string format="g")
{
const string sup_signs = "⁺⁻⁼⁽⁾ⁿ";
const string sup_digits = "⁰¹²³⁴⁵⁶⁷⁸⁹";
if(double.IsNaN(x) || double.IsInfinity(x))
{
return x.ToString();
}
int num_sign = Math.Sign(x);
x = Math.Abs(x);
// group exponents in multiples of 3 (thousands)
int exp = (int)Math.Floor(Math.Log(x, 10)/3)*3;
// otherwise use:
// int exp = (int)Math.Floor(Math.Log(x, 10));
// and handle the exp==1 case separetly to avoid 10¹
x*= Math.Pow(10, -exp);
int exp_sign = Math.Sign(exp);
exp = Math.Abs(exp);
// Build the exponent string 'dig' from right to left
string dig = string.Empty;
while(exp>0)
{
int n = exp%10;
dig = sup_digits[n] + dig;
exp = exp/10;
}
// if has exponent and its negative prepend the superscript minus sign
if(dig.Length>0 && exp_sign<0)
{
dig = sup_signs[1] + dig;
}
// prepend answer with minus if number is negative
string sig = num_sign<0 ? "-" : "";
if(dig.Length>0)
{
// has exponent
return $"{sig}{x.ToString(format)}×10{dig}";
}
else
{
// no exponent
return $"{sig}{x.ToString(format)}";
}
}
As a test case run
static void Main(string[] args)
{
// Type code here.
double x = Math.PI/50e5;
for(int i = 0; i < 20; i++)
{
// Format output to 12 wide column, right aligned
Debug.WriteLine($"{ Eng(x, "g4"),12}");
x*=50;
}
}
with the output:
628.3×10⁻⁹
31.42×10⁻⁶
1.571×10⁻³
78.54×10⁻³
3.927
196.3
9.817×10³
490.9×10³
24.54×10⁶
1.227×10⁹
61.36×10⁹
3.068×10¹²
153.4×10¹²
7.67×10¹⁵
383.5×10¹⁵
19.17×10¹⁸
958.7×10¹⁸
47.94×10²¹
2.397×10²⁴
119.8×10²⁴
By no means optimized, but it does the job. The exponents are in engineering form (multiples of 3 only, in order to avoid things like 10¹). As a bonus, the number can be formatted to a specific number of significant digits by supplying a format code like g4 or g5 for 4 or 5 digits respectively.
It can handle negative or positive numbers
It can handle negative or positive exponents of 10
In can format the mantissa
It can handle NAN or Inf.
It's in extension form for re-usability
As a follow up to my comment above - does something like this do what you require :
public String FormatAs10Power(decimal val)
{
string SuperscriptDigits = "\u2070\u00b9\u00b2\u00b3\u2074\u2075\u2076\u2077\u2078\u2079";
string expstr = String.Format("{0:0.#E0}", val);
var numparts = expstr.Split('E');
char[] powerchars = numparts[1].ToArray();
for (int i = 0; i < powerchars.Length; i++)
{
powerchars[i] = (powerchars[i] == '-') ? '\u207b' : SuperscriptDigits[powerchars[i] - '0'];
}
numparts[1] = new String(powerchars);
return String.Join(" x 10",numparts);
}
See : https://dotnetfiddle.net/dX7LAF
As per my comment above - the number is first converted to an exponential format string (https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings#EFormatString), that string is then split on the exponential separator 'E'. The first array is the numeric part, the second the power of 10 to which it is raised - this is converted to superscript characters using one of the techniques of the link I gave (Convert a string/integer to superscript in C#), converted back to a string & the two parts combined using "x 10" as the new separator.
I have assumed you want the value to single digit precision as per your example with no preceding + sign. If you need anything else you could pass the format as a parameter. The code for superscript + is '\u207A'. There is a link here (at the time of writing) giving the list of superscript codes : http://unicode.org/charts/PDF/U2070.pdf
Okay, so this is my challenge taken from CodeEval. I have to read numbers from a file that is formatted in a standard way, it has a pair of numbers separated by a comma on each line (x, n). I have to read in the pair values and process them, then print out the smallest multiple of n which is greater than or equal to x, where n is a power of 2.
EXACT REQUIREMENT: Given numbers x and n, where n is a power of 2, print out the smallest multiple of n which is greater than or equal to x. Do not use division or modulo operator.
I have come up with a number of solutions, but none of them satisfy the computer's conditions to let me pass the challenge. I only get a partial completion with scores that vary from 30 to 80 (from 100).
I'm assuming that my solutions do not pass the speed but more likely the memory-usage requirements.
I would greatly appreciate it if anyone can enlighten me and offer some better, more efficient solutions.
Here are two of my solutions:
var filePath = #"C:\Users\myfile.txt";
int x;
int n;
using (var reader = new StreamReader(filePath))
{
string numsFile = string.Empty;
while ((numsFile = reader.ReadLine()) != null)
{
var nums = numsFile.Split(',').ToArray();
x = int.Parse(nums[0]);
n = int.Parse(nums[1]);
Console.WriteLine(DangleNumbers(x, n));
}
}
<<<>>>
var fileNums = File.ReadAllLines(filePath);
foreach (var line in fileNums)
{
var nums = line.Split(',').ToArray();
x = int.Parse(nums[0]);
n = int.Parse(nums[1]);
Console.WriteLine(DangleNumbers(x, n));
}
Method to check numbers
public static int DangleNumbers(int x, int n)
{
int m = 2;
while ((n * m) < x)
{
m += 2;
}
return m * n;
}
I'm fairly new to C# and programming but these two ways I found to get the best score from several others I have tried. I'm thinking that it's not too optimal for a new string to be created on each iteration, nor do I know how to use a StringBuilder and get the values into an Int from it.
Any pointers in the right direction would be appreciated as I would really like to get this challenge passed.
The smallest multiple of n that is larger or equal to x is likely this:
if(x <= n)
{
return n;
}
else
{
return x % n == 0 ? x : (x/n + 1) * n;
}
As x and n are integers, the result of x/n will be truncated (or effectively rounded down). So the next integer larger than x that is a multiple of n is (x/n + 1) * n
Since you missed the requirements, the modulo version was the most obvious choice. Though you still got your method wrong. m = 2 would not result in the smallest being returned but it could actually be the double of the smallest if n is already larger than x.
x = 7, n = 8 would get you 16 instead of 8.
Also adding 2 to m would result in a similar problem.
x = 5, n = 2 would get you 8 instead of 6.
use the following method instead:
public static int DangleNumbers(int x, int n)
{
int result = n;
while(result < x)
result += n;
return result;
}
Still capable of begin optimized but at least right according to the (now) stated constraints.
I have tried to improve the solution with some suggestions from you guys and take the variables outside the loop and drop the ToArray() call which was redundant.
static void Main(string[] args)
{
var filePath = #"C:\Users\sorin\Desktop\sorvas.txt";
int x;
int n;
string[] nums;
using (var reader = new StreamReader(filePath))
{
string numsFile = string.Empty;
while ((numsFile = reader.ReadLine()) != null)
{
nums = numsFile.Split(',');
x = int.Parse(nums[0]);
n = int.Parse(nums[1]);
Console.WriteLine(DangleNumbers(x, n));
}
}
}
public static int DangleNumbers(int x, int n)
{
int m = 2;
while ((n * m) < x)
{
m += 2;
}
return m * n;
}
So it looks like this. The thing is that even if now the numbers have slightly improved, I got a lower score.
May it be their system to blame ?
Using the first option of reading line by line (rather than reading all lines) is clearly going to use less memory (except potentially in the case where the file is very small (eg "1,1") in which case the overhead of the reader may cause problems but at that point the memory used is probably irrelevant.
Likewise declaring the variables outside the loop is generally better but in this case since the objects are value types I'm not sure it makes a difference.
Lastly the most efficient way of doing your DangleNumbers method is probably using bitwise logic operators and the fact that n is always a power of 2. Here is my attempt:
public static int DangleNumbers3(int x, int n)
{
return ((x-1) & ~(n-1))+n;
}
Essentially it relies on the fact that in binary a power of n is always a 1 followed by zero or more zeros. Thus a multiple of n will always end in that same number of zeros. So if n has M zeros after the one then you can take the binary form of x and if it already ends in M zeros then you have your answer. Otherwise you zero out the last M digits at which point you have the multiple of n that is just under x and then you add 1.
In the code ~(n-1) is a bitmask that has M zeros at the end and the leading digits are all 1. Thus when you AND it with a number it will zero out the trailing digits. I apply this to (x-1) to avoid having to do the check for if it is already the answer and have special cases.
It is important to note that this only works because of the special form of n as a power of 2. This method avoids the need for any loops and thus should run much faster (it has five operations total and no branching at all compared to other looping methods which will tend to have at the very least an operation and a comparison per loop.
In C#, I need to work with very large (and very small) numbers, for which decimal and double are not accurate enough and BigInteger is not able to store a number's fractions.
I'd like to have the numbers to have as long components i.e. the characteristic and the mantissa, as memory (and preferably hard drive) space will allow.
Does someone have a class or is there a system type for a really big number.
I need to be able to add, subtract, divide, modulus, square, square-root, sin, cos, tan (and their inversions) and multiply the number. Pretty much the complete functionality of a standard Decimal/Double (in case I've missed any).
Infinity needn't be represented, but it would be a plus*!
An example of a very small number is:
0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001
and examples of very large numbers are:
1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001
and
-1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001
I'd prefer ToString() to return the number in the form described above. Scientific notation is acceptable, but by no means preferred.
Four most important requirements are:
the accuracy of the number
At least, basic maths operations can be
applied; multiply, division, addition and subtraction
The number must only be limited by the size of spare memory and harddrive.
Number must output as a string equivalent in the form
Characteristic, decimal point and then mantissa e.g. 100.23, 100 or
0.000000054
There should be support for reccurance in the mantissa
BigInteger is not an acceptable answer.
*If infinity is implimented, then I need only implement it as far as possible e.g. (infinity / infinity = 1), (0 / infinity = 0), etc.
Use BigInteger. It represents an arbitrarily large signed integer.
It doesn't fulfill the spec, but I would use BigInteger for integers and decimal for decimals.
BigInteger (in theory) has no upper or lower bounds.
Decimal is precise up to 28 significant figures
You may want to take a look at the BigRational class, which may be found on CodePlex.
It represents numbers as a ratio of two BigIntegers.
The ToString method doesn't meet your requirement as it formats the number as a ratio (numerator/denominator). But, since the source code is there, you can go wild implementing IFormattable.
You're asking for 2 things here that aren't currently possible in the same domain without losing some precision in your numbers. One thing is the infinity. And the other thing deals with the numerics. For example, unless you draw a line somewhere or you include some kind of pattern matching that tends towards a cutoff at a certain number, you cannot implement an "infinity".
But since you're going with really large numbers, and extremely precise small numbers, and since there is no known numeric data type that can support both, I recommend implementing your own. Just replicate the functionality of the numeric structures you already have and combine them to your needs.
My general idea is creating your own numeric data type that uses something like BigInteger for the integers and something like Decimals for the extremely precise numbers. Or you should also take a look at BigRational as they have some functionality for basic adding, subtracting, etc and they also have Numerator and Denominator properties.
The reason I'm going with Decimals over something else like Doubles is that Decimal types are fixed point and Doubles are floating point. Floating points are the reason why 0.1 + 0.2 = 0.30000000000000004, for example. But only consider this if you're completely implementing your own system.
tl;dr: Create your own class combining BigInteger and BigRational.
For my learning purposes I created class that can manage large number of digits, use it if you want. This class has some issues and I don't guarantee that it works 100% (but i test it for many scenarios and for all it was ok). Also division operator has performance issues (try to decrease divPrecision for more speed) and I don't have time right now to learn about division algorithms. I am certain there is more sophisticated (and probably much better) way to make such class but it is what I built in couple of hours based on your idea.
using System;
using System.Linq;
using System.Numerics;
namespace RealNumber
{
class RealNum
{
private BigInteger m = 0;
private int w = 0;
private static int divPrecision = 100000;
private static char[] trimStartChar = { '0', '-' };
private static char[] trimEndChars = { '.', ',' };
public RealNum()
{
}
public RealNum(BigInteger _m, int _w = 0 )
{
w = _w;
m = _m;
miniW();
}
public RealNum(string number)
{
number = number.Trim();
System.Text.RegularExpressions.Regex textValidator = new System.Text.RegularExpressions.Regex(#"^-?[0-9]+([,.][0-9]+)?$");
if (!textValidator.IsMatch(number))
{
throw new FormatException();
}
bool minSig = number.Contains('-');
number = number.TrimStart(trimStartChar);
if (number.Contains('.') || number.Contains(','))
{
number = number.TrimEnd(trimStartChar);
number = number.TrimEnd(trimEndChars);
}
if (string.IsNullOrEmpty(number))
{
return;
}
char[] splitChars = { '.', ',' };
string[] idnum = number.Split(splitChars, StringSplitOptions.None);
if (string.IsNullOrEmpty(idnum[0]))
idnum[0] = "0";
if(idnum.Length==1)
{
m = BigInteger.Parse(idnum[0]);
}
else
{
w = idnum[1].Length;
m = BigInteger.Parse(idnum[0]) * BigInteger.Pow(10, idnum[1].Length) + BigInteger.Parse(idnum[1]);
}
if (minSig)
m = -m;
miniW();
}
private void miniW()
{
while( m % (new BigInteger(10)) == 0 && m > 0 )
{
m = m / 10;
w--;
}
}
public override string ToString()
{
string num = m.ToString();
if (w > 0)
{
if(num.Length - w <= 0)
{
string zeros = new string('0', -num.Length + w + 1);
num = zeros + num;
}
num = num.Insert(num.Length - w, ".");
}
else if(w < 0)
{
string zeros = new string('0', -w);
num = num + zeros;
}
return num;
}
public static RealNum operator+ (RealNum a, RealNum b)
{
int wSub = a.w - b.w;
if(wSub<0)
{
wSub = -wSub;
a = System.Threading.Interlocked.Exchange(ref b, a);
}
return new RealNum(a.m + b.m * BigInteger.Pow(10, wSub), a.w);
}
public static RealNum operator -(RealNum a, RealNum b)
{
int wSub = a.w - b.w;
if (wSub < 0)
{
wSub = -wSub;
a = System.Threading.Interlocked.Exchange(ref b, a);
return new RealNum(b.m * BigInteger.Pow(10, wSub) - a.m, a.w);
}
return new RealNum(a.m - b.m * BigInteger.Pow(10, wSub), a.w);
}
public static RealNum operator *(RealNum a, RealNum b) =>
new RealNum(a.m * b.m, a.w+b.w);
public static RealNum operator /(RealNum a, RealNum b)
{
int precision = RealNum.divPrecision;
if (precision <= b.w)
precision = b.w+10;
int aSubSup = 0;
int aSub;
if (a.w < 0)
{
aSubSup = -a.w;
aSub = precision;
}
else
{
aSub = precision - a.w;
}
BigInteger am = a.m * BigInteger.Pow(10, aSubSup) * BigInteger.Pow(10, aSub);
return new RealNum(am/b.m, precision-b.w);
}
}
}
I coded up a program in C# to find perfect numbers within a certain range as part of a programming challenge . However, I realized it is very slow when calculating perfect numbers upwards of 10000. Are there any methods of optimization that exist for finding perfect numbers? My code is as follows:
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleTest
{
class Program
{
public static List<int> FindDivisors(int inputNo)
{
List<int> Divisors = new List<int>();
for (int i = 1; i<inputNo; i++)
{
if (inputNo%i==0)
Divisors.Add(i);
}
return Divisors;
}
public static void Main(string[] args)
{
const int limit = 100000;
List<int> PerfectNumbers = new List<int>();
List<int> Divisors=new List<int>();
for (int i=1; i<limit; i++)
{
Divisors = FindDivisors(i);
if (i==Divisors.Sum())
PerfectNumbers.Add(i);
}
Console.Write("Output =");
for (int i=0; i<PerfectNumbers.Count; i++)
{
Console.Write(" {0} ",PerfectNumbers[i]);
}
Console.Write("\n\n\nPress any key to continue . . . ");
Console.ReadKey(true);
}
}
}
Use the formula
testPerfect = 2n-1(2n - 1)
to generate possiblities then check wether the number is in fact perfect.
try this for some bedtime reading
Do perfect numbers change? No. Look here. Surely, they should be calculated once and then stored.
In your case, the only results will be
6
28
496
8128
The next one is 33550336. Outside your range.
Just the obvious one from me: you don't need to check every divisor. No point looking for divisors past inputNo/2. That cuts down half of the calculations, but this is not an order of magnitude faster.
One way to solve things like this involves building a huge array in memory of every number, and then crossing numbers out.
if your still looking for something to calculate perfect numbers.
this goes through the first ten thousand pretty quick, but the 33 million number is a little slower.
public class Perfect {
private static Perfect INSTANCE = new Perfect();
public static Perfect getInstance() {
return INSTANCE;
}
/**
* the method that determines if a number is perfect;
*
* #param n
* #return
*/
public boolean isPerfect(long n) {
long i = 0;
long value = 0;
while(++i<n){
value = (0 == n%i?value+i:value);
}
return n==value;
}
}
For anyone interested in a LINQ based approach, the following method worked quite well and efficiently for me in determining whether or not a caller supplied integer value is a perfect number.
bool IsPerfectNumber(int value)
{
var isPerfect = false;
int maxCheck = Convert.ToInt32(Math.Sqrt(value));
int[] possibleDivisors = Enumerable.Range(1, maxCheck).ToArray();
int[] properDivisors = possibleDivisors.Where(d => (value % d == 0)).Select(d => d).ToArray();
int divisorsSum = properDivisors.Sum();
if (IsPrime(divisorsSum))
{
int lastDivisor = properDivisors.Last();
isPerfect = (value == (lastDivisor * divisorsSum));
}
return isPerfect;
}
For simplicity and clarity, my implementation for IsPrime(), which is used within IsPerfectNumber(), is omitted.
To continue from Charles Gargent's answer there is a very quick way to check if a Mersenne Number a.k.a. 2^n - 1 is prime. It is called the Lucas-Lehmer test
The basic pseudocode though (taken from the Wikipedia page) is:
// Determine if Mp = 2p − 1 is prime for p > 2
Lucas–Lehmer(p)
var s = 4
var M = 2p − 1
repeat p − 2 times:
s = ((s × s) − 2) mod M
if s == 0 return PRIME else return COMPOSITE