Предыстория такова. Хотел на С#-юниора устроиться, проходил телефонное интервью. И среди прочего интервьюер спросил "а вот если тебе надо посчитать точный факториал некоего большого числа, например, 2000, как будешь делать?". Я ответил, что мол, напишу класс для работы с целыми числами любой длины и потом факториал с ними посчитаю. О, говорит, он, круто, давай напиши такое тестовое задание, присылай нам и на собеседование приходи, там обсудим. Вот я и написал нижеследующее, пришёл на собеседование, вроде поговорили, обсудили. И потом всё стихло, ни звонков, ничего. Сейчас-то я работаю в другом месте, всё ок. Но интересно стало, это насколько плох мой код, из-за него меня не взяли, или уже на собеседовании облажался, или просто не понравился.
Покритикуйте, пожалуйста. Что класс недоделан я знаю, вчастности нет оператора -. Ну и си-стиль наверное прёт. Но главное мне было нужно факториал сделать. Считает вроде правильно, по интернет-калькулятору факториалов проверял.
Заранее спасибо.
using System;
using System.Collections.Generic;
namespace bigint
{
class BigInt
{
public BigInt() { }
public BigInt(int val)
{
isPos_ = (val >= 0);
int v = Math.Abs(val);
while (v > 0)
{
val_.Add((byte)(v % 10));
v /= 10;
}
}
public BigInt(BigInt val)
{
isPos_ = val.isPos_;
val_.AddRange(val.val_);
}
public static BigInt operator+(BigInt val1, BigInt val2)
{
if (val1.isPos_ == val2.isPos_)
return Add(val1, val2);
else
return Deduct(val1, val2);
}
public static BigInt operator+(BigInt val1, int val2)
{
return val1 + new BigInt(val2);
}
public static BigInt operator*(BigInt val, int n)
{
BigInt retVal = new BigInt() { isPos_ = n >= 0 ? val.isPos_ : !val.isPos_};
int absN = Math.Abs(n);
if (absN == 0)
return retVal;
if (absN == 1)
return retVal + val;
int sqrt = (int)Math.Floor(Math.Sqrt(absN));
BigInt tmp = new BigInt(val);
for (int i = 0; i < sqrt - 1; ++i)
tmp = tmp + val;
for (int i = 0; i < absN / sqrt; ++i)
retVal = retVal + tmp;
for (int i = 0; i < absN % sqrt; ++i)
retVal = retVal + val;
return retVal;
}
public static BigInt Factorial(int val)
{
BigInt retVal = new BigInt(1);
for (int i = 2; i <= val; i++)
retVal = retVal * i;
return retVal;
}
private static Dictionary<int, BigInt> factsCache_ = new Dictionary<int, BigInt>();
public static BigInt FactorialWithCache(int val)
{
if (val <= 1)
return new BigInt(1);
if (!factsCache_.ContainsKey(val))
factsCache_.Add(val, FactorialWithCache(val - 1) * val);
return factsCache_[val];
}
public string GetValue(bool inExpFormat = false)
{
string retVal = "";
if (val_.Count == 0)
return "0";
if (!inExpFormat)
{
foreach (var n in val_)
retVal = n.ToString() + retVal;
if (!isPos_)
retVal = "-" + retVal;
}
else
{
for (int i = Math.Max(val_.Count - 9, 0); i < val_.Count; ++i)
{
if (i == val_.Count - 1)
retVal = "." + retVal;
retVal = val_[i] + retVal;
}
retVal += " x 10^" + (val_.Count - 1).ToString();
if (!isPos_)
retVal = "-" + retVal;
}
return retVal;
}
private static BigInt Add(BigInt val1, BigInt val2)
{
BigInt retVal = new BigInt(val1) { isPos_ = val1.isPos_ };
int minSize = Math.Min(retVal.val_.Count, val2.val_.Count);
bool trans = false;
for (int i = 0; i < minSize; ++i)
{
int n = retVal.val_[i] + val2.val_[i];
if (trans)
++n;
trans = (n > 9);
retVal.val_[i] = (byte)(n % 10);
}
if (retVal.val_.Count < val2.val_.Count)
for (int i = minSize; i < val2.val_.Count; ++i)
retVal.val_.Add(val2.val_[i]);
if (trans)
{
int i = minSize;
while (trans)
{
if (retVal.val_.Count == i)
{
retVal.val_.Add(1);
trans = false;
}
else
{
int n = retVal.val_[i] + 1;
trans = (n > 9);
retVal.val_[i] = (byte)(n % 10);
}
++i;
}
}
return retVal;
}
private static BigInt Deduct(BigInt val1, BigInt val2)
{
bool isGrt = isGreaterOrEqual(val1.val_, val2.val_);
BigInt retVal = new BigInt(isGrt ? val1 : val2) { isPos_ = isGrt ? val1.isPos_ : !val1.isPos_ };
int minSize = Math.Min(val1.val_.Count, val2.val_.Count);
bool trans = false;
for (int i = 0; i < minSize; ++i)
{
int n = retVal.val_[i] - (isGrt ? val2.val_[i] : val1.val_[i]);
if (trans)
--n;
trans = (n < 0);
if (n < 0)
n += 10;
retVal.val_[i] = (byte)(n % 10);
}
int ii = retVal.val_.Count - 1;
while (ii >= 0 && retVal.val_[ii] == 0)
{
retVal.val_.RemoveAt(ii);
--ii;
}
if (retVal.val_.Count == 0)
retVal.isPos_ = false;
return retVal;
}
private static bool isGreaterOrEqual(List<byte> l1, List<byte> l2)
{
if (l1.Count > l2.Count)
return true;
if (l1.Count < l2.Count)
return false;
if (l1.Count == 0)
return true;
for (int i = l1.Count -1; i >= 0; --i)
{
if (l1[i] > l2[i])
return true;
if (l1[i] < l2[i])
return false;
}
return true;
}
private List<byte> val_ = new List<byte>();
private bool isPos_ = true;
}
}
Использование:
static void Main(string[] args)
{
Console.SetOut(new System.IO.StreamWriter("fact.log", true));
for (int i = 0; i <= 5000; ++i)
Console.WriteLine(string.Format("{0:00000}!: {1}", i, BigInt.FactorialWithCache(i).GetValue()));
}