Re[3]: Покритикуйте тестовой задание про факториал.
От: bl-blx Россия http://yegodm.blogspot.com
Дата: 31.08.12 13:34
Оценка: 3 (1)
Здравствуйте, Заяц, Вы писали:

BB>>- почему List<byte>? почему не массив?

З>Так ведь нужен набор переменной длины.
З>Да и в msdn написано, что "The List<T> class is the generic equivalent of the ArrayList class." А про ArrayList — что это просто динамический массив "Implements the IList interface using an array whose size is dynamically increased as required".

Для хранения-то можно и в массив конвертировать. У вас же immutable объект, как я понял.

BB>>- вычисление факториала, очевидно, может пользоваться ближащим закэшированным значением;

З>Второй метод FactorialWithCache так и делает. Рекурсивно вызвав себя сколько-то раз он дойдёт до ближайшего закэшированного.
З>Вроде доп. расходы на вызовы и проверки каждый раз по кэшу не должны быть сильно большими на фоне перемножений. Или я ошибаюсь?

Чтобы в этом убедиться нужно измерить производительность. Можно, конечно, попытаться теоретически
порассуждать, но едва ли это будет эффективнее тестирования по времени и результатам.

Рекурсии я бы избегал — слишком медленно получается.
Наличие двух методов Factorial и FactorialWithCache тоже не очень хороший знак.
Я бы сделал один, но с параметром. Что-то типа:
enum FactorialComputationKind 
{
    SimpleMultiplication,
    CachedMultiplication,
    Approximation
}

public static BigInt Factorial(int val, FactorialComputationKind computationKind = FactorialComputationKind.CachedMultiplication)

BB>>- Getvalue(bool inExpFormat) — зачем так? почему не ToString(string format)?
З>Это просто так у меня получилось, на юниора ж шёл. Конечно надо всё делать со стандартными названиями.

Тут дело скорее не в названии, а в использовании стандартного подхода к форматированию.
И да, забыл сюда же добавить использование StringBuilder вместо конкатенации, использованной вами.

Z>>>Считает вроде правильно, по интернет-калькулятору факториалов проверял.

BB>>Где тесты, которые это подтверждают?
З>Тесты были, просто здесь не приводил, слишком большие строки там. Проверял просто — несколько значений, типа для 100, 1000 и 5000 взял с калькулятора факториалов и с ними сравнил.

Жаль, что вы так сразу и не написали.
El pueblo unido jamás será vencido.
Re: Покритикуйте тестовой задание про факториал.
От: Философ Ад http://vk.com/id10256428
Дата: 04.09.12 08:36
Оценка: +1
Внутрь методов почти не заглядывал.
Приведу первое, что бросилось в глаза.

Здравствуйте, zayats, Вы писали:

Z>

Z>namespace bigint
Z>{
Z>    class BigInt
Z>    {
          // 1) Нарушение SRP: мух от котлет нужно отделять, т.е. числа отдельно - вычислительные алгоритмы отдельно
          //посмотрите в сторону System.Math
          // 2) Имена методов должны начинаться с глагола, т.е. не Factorial() а ComputeFactorial()
          //     в таком случае второй метод может называться GetFactorial()
          //     не нужно в имени метода описывать способ которым вы что-то делаете, просто само действие
Z>        public static BigInt Factorial(int val)
Z>        public static BigInt FactorialWithCache(int val)
Z>        private static Dictionary<int, BigInt> factsCache_ = new Dictionary<int, BigInt>();

          // 3) Неверное, вводящее в заблуждение, название метода.
          //    этот метод не возвращает значение, он само число преобразует в строку,
          //    соответственно должен называться или ConvertToString() или просто string Convert()
          //    но будет значительно лучше, если он будет называться просто ToString()
          //    и при этом будет переопределять соответствующий виртуальный метод System.Object
          // 4) Загадочные сокращения в именах inExpFormat - в экспериментальном формате что-ли? 
          //    как минимум должно быть "еxponential"
Z>        public string GetValue(bool inExpFormat = false)

Z>}



//5) Вот это не тест.
[s]Z>static void Main(string[] args)
Z>{
Z>     Console.SetOut(new System.IO.StreamWriter("fact.log", true));
            
Z>     for (int i = 0; i <= 5000; ++i)
Z>         Console.WriteLine(string.Format("{0:00000}!: {1}", i, BigInt.FactorialWithCache(i).GetValue()));
Z>}
Z>
[/s]


// В тесте я хочу увидеть что-то типа вот этого:

// BigInt computed = MathHelper.GetFactoial ( 2000 );
// BigInt tableValue = new BigInt
// (
// "331627509245063324117539338057632403828111720810578039457193"
// + "543706038077905600822400273230859732592255402352941225834109"
// + "258084817 415293796131386633526343688905634058556163940605117"
// + "252571870647856393544045405243957467037674108722970434684158"
// + "3437524315808775336451274 87995436859247408032408946561507233....дальше много-много-много цифр"
// );

// Assert.AreEqual(computed, tableValue);
Всё сказанное выше — личное мнение, если не указано обратное.
Покритикуйте тестовой задание про факториал.
От: zayats  
Дата: 31.08.12 07:17
Оценка:
Предыстория такова. Хотел на С#-юниора устроиться, проходил телефонное интервью. И среди прочего интервьюер спросил "а вот если тебе надо посчитать точный факториал некоего большого числа, например, 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()));
}
Re: Покритикуйте тестовой задание про факториал.
От: HowardLovekraft  
Дата: 31.08.12 07:22
Оценка:
Здравствуйте, zayats, Вы писали:

Z>skipped

А как давно дело было? А то вот...
Re[2]: Покритикуйте тестовой задание про факториал.
От: zayats  
Дата: 31.08.12 07:32
Оценка:
Здравствуйте, HowardLovekraft, Вы писали:

HL>А как давно дело было? А то вот...

Это знаю, да. Задача была именно сделать с нуля. Дело было не очень давно.
Re: Покритикуйте тестовой задание про факториал.
От: GGoga  
Дата: 31.08.12 07:39
Оценка:
Здравствуйте, zayats, Вы писали:

Z>Предыстория такова. Хотел на С#-юниора устроиться, проходил телефонное интервью...


Фирма случаем не в Киеве находится?
Re[2]: Покритикуйте тестовой задание про факториал.
От: zayats  
Дата: 31.08.12 07:43
Оценка:
Здравствуйте, GGoga, Вы писали:

Z>>Предыстория такова. Хотел на С#-юниора устроиться, проходил телефонное интервью...

GG>Фирма случаем не в Киеве находится?
Нет, дело в Москве происходило.
Re: Покритикуйте тестовой задание про факториал.
От: Аноним  
Дата: 31.08.12 08:00
Оценка:
Здравствуйте, zayats, Вы писали:

Z>Покритикуйте, пожалуйста. Что класс недоделан я знаю, вчастности нет оператора -. Ну и си-стиль наверное прёт. Но главное мне было нужно факториал сделать. Считает вроде правильно, по интернет-калькулятору факториалов проверял.

Z>Заранее спасибо.

http://en.wikipedia.org/wiki/Schönhage–Strassen_algorithm

Много лишнего кода. Код не читаемый. Постфиксы.
Re: Покритикуйте тестовой задание про факториал.
От: Аноним  
Дата: 31.08.12 09:17
Оценка:
Здравствуйте, zayats, Вы писали:

Дело не в стиле, и то что c++ прёт итп.
Это всё мелочи.
Существуют алгоритмы быстрого умножения (с помощью БПФ, или другие может быть).
Re: Покритикуйте тестовой задание про факториал.
От: IObserver Ниоткуда  
Дата: 31.08.12 10:14
Оценка:
Здравствуйте, zayats, Вы писали:

Z>Я ответил, что мол, напишу класс для работы с целыми числами любой длины и потом факториал с ними посчитаю.


И вы правда думаете, что нет библиотек с открытым кодом, которые можно использовать? То есть никому на свете до вас это не понадобилось?
=сначала спроси у GPT=
Re: Покритикуйте тестовой задание про факториал.
От: bl-blx Россия http://yegodm.blogspot.com
Дата: 31.08.12 10:15
Оценка:
Здравствуйте, zayats, Вы писали:

Z>Покритикуйте, пожалуйста. Что класс недоделан я знаю, вчастности нет оператора -. Ну и си-стиль наверное прёт. Но главное мне было нужно факториал сделать.

Навскидку:
1) стиль
— поля в конце, постфиксы, приватные методы с заглавной, тернарный оператор
2) качество:
— immutable поля без readonly;
— почему List<byte>? почему не массив?
— вычисление факториала, очевидно, может пользоваться ближащим закэшированным значением;
— Getvalue(bool inExpFormat) — зачем так? почему не ToString(string format)?

Z>Считает вроде правильно, по интернет-калькулятору факториалов проверял.


Где тесты, которые это подтверждают?
El pueblo unido jamás será vencido.
Re[2]: Покритикуйте тестовой задание про факториал.
От: Аноним  
Дата: 31.08.12 10:21
Оценка:
Здравствуйте, Аноним, Вы писали:

А>http://en.wikipedia.org/wiki/Schönhage–Strassen_algorithm


>In practice the Schönhage–Strassen algorithm starts to outperform older methods such as Karatsuba and Toom–Cook multiplication for numbers beyond 2215 to 2217 (10,000 to 40,000 decimal digits).


2000! < 2000^2000 < 10^8000

А>Много лишнего кода. Код не читаемый. Постфиксы.


А это да, правда.
Re[2]: Покритикуйте тестовой задание про факториал.
От: Заяц  
Дата: 31.08.12 11:27
Оценка:
Здравствуйте, IObserver, Вы писали:

IO>И вы правда думаете, что нет библиотек с открытым кодом, которые можно использовать? То есть никому на свете до вас это не понадобилось?

Уверен, что библиотеки есть. Так я же не для практических целей, а как тестовое задание.
Re[3]: Покритикуйте тестовой задание про факториал.
От: pentagra  
Дата: 31.08.12 11:33
Оценка:
Здравствуйте, Заяц, Вы писали:

З>Здравствуйте, IObserver, Вы писали:


IO>>И вы правда думаете, что нет библиотек с открытым кодом, которые можно использовать? То есть никому на свете до вас это не понадобилось?

З>Уверен, что библиотеки есть. Так я же не для практических целей, а как тестовое задание.

Но на собеседовании-то спрашивали не "какое бы вам тестовое задание дать", а "а вот если тебе надо посчитать точный факториал некоего большого числа, например, 2000, как будешь делать?"
Re[2]: Покритикуйте тестовой задание про факториал.
От: Заяц  
Дата: 31.08.12 12:11
Оценка:
Здравствуйте, bl-blx, Вы писали:

Спасибо за сообщение.

Z>>Покритикуйте, пожалуйста. Что класс недоделан я знаю, вчастности нет оператора -. Ну и си-стиль наверное прёт. Но главное мне было нужно факториал сделать.

BB>Навскидку:
BB>1) стиль
BB>- поля в конце, постфиксы, приватные методы с заглавной, тернарный оператор
Ок.
BB>2) качество:
BB>- immutable поля без readonly;
Ок.
BB>- почему List<byte>? почему не массив?
Так ведь нужен набор переменной длины.
Да и в msdn написано, что "The List<T> class is the generic equivalent of the ArrayList class." А про ArrayList — что это просто динамический массив "Implements the IList interface using an array whose size is dynamically increased as required".

BB>- вычисление факториала, очевидно, может пользоваться ближащим закэшированным значением;

Второй метод FactorialWithCache так и делает. Рекурсивно вызвав себя сколько-то раз он дойдёт до ближайшего закэшированного.
Вроде доп. расходы на вызовы и проверки каждый раз по кэшу не должны быть сильно большими на фоне перемножений. Или я ошибаюсь?

BB>- Getvalue(bool inExpFormat) — зачем так? почему не ToString(string format)?

Это просто так у меня получилось, на юниора ж шёл. Конечно надо всё делать со стандартными названиями.

Z>>Считает вроде правильно, по интернет-калькулятору факториалов проверял.

BB>Где тесты, которые это подтверждают?
Тесты были, просто здесь не приводил, слишком большие строки там. Проверял просто — несколько значений, типа для 100, 1000 и 5000 взял с калькулятора факториалов и с ними сравнил.
Re[4]: Покритикуйте тестовой задание про факториал.
От: Заяц  
Дата: 31.08.12 12:15
Оценка:
Здравствуйте, pentagra, Вы писали:

P>Но на собеседовании-то спрашивали не "какое бы вам тестовое задание дать", а "а вот если тебе надо посчитать точный факториал некоего большого числа, например, 2000, как будешь делать?"

Согласен, я же не полность дословно разговор приводил. Смысл моего ответа был — надо использовать какой-нибудь класс для работы с большими числами и в нём посчитать. На что был ответ — ну вот и давай ты сделаешь такой класс, и факториал заодно в качестве тестового задания. Может конечно я всё не так понял и от меня именно просто использование всего стандартного хотели.
Re: Покритикуйте тестовой задание про факториал.
От: matumba  
Дата: 03.09.12 11:58
Оценка:
Здравствуйте, zayats, Вы писали:

Z>Предыстория такова. Хотел на С#-юниора устроиться, проходил телефонное интервью. И среди прочего интервьюер спросил "а вот если тебе надо посчитать точный факториал некоего большого числа, например, 2000, как будешь делать?"


Если вопрос был в контексте чего-то, это "чего-то" надо писать. Если просто "от балды", то возможно хотели проверить, кто на том конце — велосипедист, математик или разумный инженер:
Велосипедист: сразу бросится писать свой факториал с блэкджеком и хэшами. Такой сразу идёт в зад, потому что на фирме он должен выдавать быстрый и качественный результат, а не писать каждую ерунду с нуля.
Математик: Тоже, вобщем-то, узколобый для продакшена — сразу полезет в учебники искать сокращённое умножение и т.п., т.е. будет пытаться сделать то же, что и велосипедист, но чуть более продвинуто.
Инженер: сразу начнёт гуглить на пример ГОТОВОГО РЕШЕНИЯ, ибо не он первый, кому понадобились факториалы. Возможно даже, решение давно уже есть в самом .NET; И только потом, не найдя решения, можно браться за ручную работу. Опять же, может даже не сразу в математику, а как топикстартер — на кэше. И только после оценки "недостаточная производительность" лезть в математику.

Но даже с т.з. этого конкретного задания, вы его сделали "overqualified": вам что, дали задание "математическая библиотека"? Нет. Тогда зачем вам эти "большие цифры"? Простая функция "умножить нивротчисло на нивротчисло" решает все проблемы. Это тоже важно: насколько разумно вы умеете решать конкретные задачи. Антипример: "работяги" из Оперы, потратившие уйму времени на "свои контролы", в результате чего до сих пор лажают на элементарном взаимодействии — ребята "переусердствовали" в решении простой задачи: ДВУплатформенный браузер.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.