Pull to refresh

Comments 50

Спасибо за статью, полезно. Я бы результат ранд2 принял бы за бинарный рандом и ранд3 реализовал бы через сдвиги:
int Rand3() {
    return rand2() | (rand2() << 1);
}
Хотя нет, извините, нельзя конечно же. Будет еще нежелательное 3 при двух единицах
static int rand3() => 
    (int) ((rand2() + rand2() * 2 + rand2() * 4 + rand2() * 8 + rand2() * 16 + 
            rand2() * 32 + rand2() * 64) / 128.0 * 3);

Это же аппроксимированное решение, вероятности не будут одинаковыми.

UFO just landed and posted this here
не, 25% — 0 и 0, 25% — 1 и 1, 25% — 1 и 0 и 25% — 0 и 1. While как раз откинет одну пару 0 и 1 для равновероятного появление 0 1 и 2.
Увы нет. В скобках 2^5 вариантов, т.е. 32 различных варианта. Это не кратно 3.
Тут распределение не будет равномерным — единичка выпадает на примерно 3% реже, чем 0 и 2
Разве вероятность появления в скобках числа от нуля до пяти не будет одинакова для каждого числа?

С чего бы? Ваши вероятности для того, что в скобках — такие:


1/32, 5/32, 10/32, 10/32, 5/32, 1/32


По модулю 3 получаются более равномерные, но все еще не равные вероятности:


11/32, 10/32, 11/32

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

Получить 0 или 5 в скобках можно только 1 способом — все слагаемые должны быть 0 или 1 соответственно.


Получить 1 или 4 можно 5 способами — одно из слагаемых должно отличаться от остальных.


Получить 2 или 3 можно 10 способами — потому что 5!/2!3!, подробно объяснять лень.

int rand3()
        {
            switch (rand2().ToString() + rand2().ToString()) {
                case "00":
                    return 0;
                break;
                case "01":
                    return 1;
                break;
                case "10":
                    return 2;
                break;
                case "11":
                    return rand3();
                break;
                default:
                    return rand3();
            }
        }
int rand3()
        {
            int v= (rand2() + rand2()) {
            return v>2? rand3(): v  
        }
rand2() + rand2() никогда не больше 2
rand2() + rand2() +rand2() так уж и быть)
Судя по тому, что самый популярный ответ именно по Random, можно предположить, что вопросы, либо скучнейшие или… мы что то не догоняем, еще есть вариант — зачем мне это??)
Я думал что есть какой-то красиывый вариант, но, видимо, нет его. Нужно пергениерировать и писать циклы и условия.
Вижу что ошибся, 2 выпадет чаще, чем 1 или 0
1 же…
Рассмотрим варианты появления:
0: при 0+0
1: при 1+0 или 0+1
2: при 1+1

Вероятности:
0: 1/2*1/2 = 1/4
1: 1/2*1/2 + 1/2*1/2 = 2/4 = 1/2
2: 1/2*1/2 = 1/4

Следовательно вероятность появления 1 — 50%, 0 и 2 — 25%…
О очередной «i++ + i++» с собеседований запостили.

1) детали имплементации рантайма .NET, есть еще Mono, еще есть Mono под LLVM, есть еще отнсительно новый RyuJIT
2) О май гад, делегат это тип, в рот мне тапки. В каждой книге по С# об этом говорят.
3) Дети, а теперь давайте поработаем интерпретатором
4) З — Замыкания. Надеюсь автор знал правильный ответ.
5) Хрен проссышь что там тильда а не минус, пятерочка за крипто-операторы вроде бинарного комплемента (~) и отрицания (!).
6) GC.Collect() нет смысла вызывать никогда

Тогда вопрос автору, как сделать каст без боксинга и создания новых объектов в выражении:
// не меняя сигнатуру естественно
public static int CastToInt<T>(T value)
{
    return (?)value;
}

Ближе к вечеру кину правильный ответ. Зачем портить удовольствие тому кто попытается его найти.
Вообще для того, чтобы писать хороший код, нужно понимать как работает фреймворк и среда исполнения. Это особенно важно, если вы hft трейдер, например. Пишете на с++, разбирайтесь как работает компилятор. Пишете под mono, читайте про mono. Это позволит понять, как ваш код оптимизировать. А подход «оно и так работает» мне лично непонятен. В конце концов вы же и заинтересованы в том, чтобы ваше приложение работало быстро и стабильно.
>Вообще для того, чтобы писать хороший код, нужно понимать как работает фреймворк и среда исполнения.
Согласен. Но примеры в статье не про хороший код.
3) комбинация делегатов и использования результата вызова комбинации делегатов
4) замыкания, неявное создание объектов в куче
5) использования редкого оператора, где его визуально можно спутать с другим оператором (-)
6) Явные вызок GC.Collect() плохо пахнет даже с отговорками

Как человек, который уже третий год работает в финансовой сфере (не подвального уровня, а в компаниях международного уровня) могу с увереностью сказать, что гавнокода тут не меньше, чем в проектах других представителей энтерпрайз сектора, а может и больше. И поверьте мне на слово, трейдеров это не так сильно волнует, к сожалению… Самого это жутко бесит, но это реалии. Для обработки же большого кол-ва ордеров сейчас делается упор на количестве железа и масштабируемых системах, а не на «выжимке» из железа максимума… Им проще докупить сервер, чем платить специалисту за знание тонкостей работы железа, ОС и прочего. Касательно статьи, спасибо, большая часть и вправду может быть полезной, но не всё:

1) Возможно, интересно знать, но на практике есть этим знаниям применение? Не думаю… Меня как-то на собеседовании спросили, какой бит используется GC для маркировки обьекта после прохода по нему (при анализе, что нужно собрать). На кой, спрашивается, это нужно знать? Возможно, однажды, будет какой-то кейс где это может как-то пригодиться, но на такие случаи достаточно это один раз загуглить и забыть, а не держать такие знания в памяти…
4) Замыкание же. Обьезженая тема, как по мне. Главное понимать как это работает на практике, я думаю, а превращается ли это в отдельный клас в нутрях, важно ли это? Если да, то как часто?

Вообще, оглядываясь назад, на большинстве моих собеседований пытались узнать чего я не знаю, а не что мне известно. В большинстве ситуаций это просто было своего рода рычагом, чтобы снизить требуюмую изначально ЗП. Не так важно понимать, что человек знает, сколько что он может/умеет делать. Ведь не всегда наличие знаний подразумевает умение ними пользоваться… В этом контексте большинство нынешних собеседований не так показательны, к сожалению…
public static int CastToInt(T value)
{
return (int)(dynamic)value;
}

Скомпилируется и скастит, скажем, double в int. Для остального (типа стринга) пошлёт с эксешпеном :)

Боксинг всё равно будет. Компилятор генерирует такой код:


public static int CastToInt<T>(T v)
{
    if (Test.<>o__0<T>.<>p__0 == null)
    {
        Test.<>o__0<T>.<>p__0 = CallSite<Func<CallSite, object, int>>.Create(Binder.Convert(CSharpBinderFlags.ConvertExplicit, typeof(int), typeof(Test)));
    }
    return Test.<>o__0<T>.<>p__0.Target(Test.<>o__0<T>.<>p__0, v);
}

Где Target имеет тип Func<CallSite, object, int>. А при первом вызове к тому же будут созданы новые объекты.

Аналог (int)(object)value без боксинга будет таким:


public static int CastToInt2<T>(T v)
{
     return __refvalue(__makeref(v), int);
}

Оба варианта работают только когда typeof(T) == typeof(int). Для непрямых приведений (например, из double к int) можно написать что-то вроде:


public static int CastToInt2<T>(T v)
{
    if (typeof(T) == typeof(int))
        return __refvalue(__makeref(v), int);
    else if (typeof(T) == typeof(double))
        return (int) __refvalue(__makeref(v), double);
    // Similar conditions vor all possible casts
    else
        throw new InvalidCastException();
}
Да, это правильный вариант "__refvalue(__makeref(v), int)".
Других вариантов без лишних аллокаций нет.

На самом деле (int)(object)value тоже может работать без выделения памяти, в зависимости от версии JIT. У меня получается такой код в x86 Release:
Boxing elimination JIT x86 Release


Для сравнения (object)value:
Boxing JIT x86 Release


А вот вариант с __refvalue(__makeref(v), int), тут кода уже больше и тоже есть внешний вызов:
Refvalue JIT x86 Release


Этот же код в x64 Release, (int)(object)value опять побеждает:
Boxing elimination JIT x64 Release
Boxing JIT x64 Release
Refvalue JIT x64 Release


Не зря говорят, что преждевременная оптимизация — корень всех зол.

Это сравнение того кода который сгенерит JIT компилятор.
Ему уже известно что вместо дженерика будет int, и кастить int в int он не будет.
Но, этот компилятор не будет, или в этой версии не будет. Это надежда на оптимизации компилятора. Не спорю что полезно о них знать, но оптимизации могут и не случиться, и приложение начнет засирать память.

Хотя наблюдение довольно интересное.
Бонус так решил:
 static class A
 {
     static Random r = new Random();
     static int c = 0;
     public static int rand3()
     {
         c = (c + 1) % 3;
         while (r.Next(2) == 0)
             c++;
         return c % 3;
     }
 }

Проверка:
> Enumerable.Range(0, 10000).Select(_ => A.rand3()).GroupBy(_ => _).Select(g => g.Count()).ToArray()
int[3] { 3365, 3300, 3335 }
Вас не смущает, то что весь цикл while можно убрать и ваш тест не изменится?
Точнее заменить while на if. Спасибо, сразу не сообразил.
Уж лучше оставить while. Здесь проблема в том, что имея такой генератор случайных чисел, можно с какой-то вероятностью, большей чем 0.3 предсказывать значение следующего числа.

Спасибо за статью, полезно.
Предлогаю свое решения последнего вопроса.
int rand3() {
int r = rand2();
return r == 0? r: r + rand2();
}

0 будет выпадать с вероятностью 0.5, 1 и 2 по 0.25. Совсем не то, что просили
Вопрос по третьему пункту. Зачем нужен MulticastDelegate, если у обычного делегата уже есть Cobine(+=)?
Цитата:
Любые типы делегатов — это потомки класса MulticastDelegate, от которого
они наследуют все поля, свойства и методы.

Класс System.MulticastDelegate является производным от класса System.Delegate,
который, в свою очередь, наследует от класса System.Object. Два класса делегатов
появились исторически, в то время как в FCL предполагался только один.
public int rand3()
{
// 0 -> 0.5, 1 -> 0.5, 2 -> 0
var x = rand2();
// 0.5 * 0.5 = 0.25 -> 0, 1
if(x == rand2())
{  
  // 0.25 * 0.5 -> 0.75 = 1/3
  return x == rand2() ? 2 : x;
}
return x;
}
Sign up to leave a comment.

Articles