Loading...
galants avatar galants 12 Точки

[Homework] C# Basics - Operators-Expressions-and-Statements - Problem{21} - Bit Sifting

Здравейте колеги,

В условието на задачата е записано, че след превинаване на числото прес "ситото", трябва да се преброят битовете със стойност '1'. Първото което ми хрумна като идея е следното:

Вариант 1

 

int bitsCount = 0;
ulong number = ulong.Parse(Console.ReadLine());

...

while(number > 0)
{
      if(1==(number & 1))
                  bitsCount++;

      number = number>>1;
}

но след допълнителни размишления и рефакторинг стигнах до др. два варианта на преброяването на '1'. Ето ги и тях:

Вариант 2

int bitsCount = 0;
ulong number = ulong.Parse(Console.ReadLine());

...

while (number > 0)
{
      bitsCount += (number & 1);
      number = number >> 1;
}

Вариант 3

int bitsCount = 0;
ulong number = ulong.Parse(Console.ReadLine());

...

while (number != 0)
{
      number &= (number - 1);
      bitsCount += 1;
}

Въпросът ми е кой от трите варианта е по-бърз и удачен?

Според мен първият вариант не е удачен тъй като има if конструкция, която намалява производителността на процесора, тъй като се нарушава конвейера от инструкции.

Третия вариант е удачен, но е малко труден за разбиране. (идеята за него ми дойде, когато разиграх възможностите на лист хартия).

Според мен най-удачният вариат е вторият, тъй като при него ако първият бит е '1' се добавя към брояча и след това се иместват битовете на дясно. Също така може много лесно да се направи да брои '0'.

Очаквам Вашите коментари.

Тагове:
2
Programming Basics
milkokochev avatar milkokochev 10 Точки
Аз лично съм ползвал първия вариант. Според мен е въпрос на личен избор в случая, зависи на кой каква логика му е удобна за разбиране. Относно бързината, надали ще се затрудни процесора, каквото и да се прави в задачата, но е факт, че едобре да се знае същината на процесите кое какво прави.
2
Ellnita avatar Ellnita 49 Точки

Аз съм съгласна с теб, че вторият вариант изглежда най-чист. Няма if конструкция и не е труден за разбиране. Но също така съм съгласна с колегата Кочев, че е важно как тече твоята собствена мисъл. Важно е да се разбира добре логиката зад написаните редове. Както в математиката - ако разбираш добре какво се случва, то можеш да го приложиш в много различни ситуации с леки адаптации и да се справяш и с по-големи проблеми. Производителността на процесора няма да се повлияе значително от един вариант на друг от тези три (според мен), докато разбирането на задачата и удобството са с приоритет. Аз съм ползвала вариант 2, но всеки може да си избере каквото му е удобно.

2
KatyaMarincheva avatar KatyaMarincheva 572 Точки

Здравейте колеги,

С изненада откривам, че не само мен ме е заинтригувала идеята да намеря различни начини за преброяване на битовете със стойност '1' - т.е. вече има такава тема.

Аз открих до момента 8 различни начина за преброяване и ги тесвах в judge за performance, тъй като иначе като резултат - всички дават 100 точки.

Това е цялото решение с закоментирани допълнителни методи за преброяване.

Ето и резултатите от класацията:

    // first "1"-bits counting algorithm
        for (int i = 0; i < 64; i++)
        {
            ulong result = number & 1;
            if (result == 1)
            {
                counter++;
            }
            number = number >> 1;
        }

Memory: 8.21 MB
Time: 0.010 s

 

    // second "1"-bits counting algorithm 
        for (int i = 0; i < 64; i++)
        {
            mask = (ulong)1 << i;
            ulong result = number & mask;
            if (result == mask)
            {
                counter++;
            }
        }

Memory: 8.20 MB
Time: 0.011 s

 


// sixth "1"-bits counting algorithm
        counter = binary.Split('1').Length - 1;

Memory: 8.29 MB
Time: 0.011 s

 

    // seventh "1"-bits counting algorithm
        counter = binary.Length - (binary.Replace("1", "").Length);

Memory: 8.30 MB
Time: 0.012 s

 

    // third "1"-bits counting algorithm
        for (int i = 0; i < binary.Length; i++)
        {
            if (binary[i] == '1')
            {
                counter++;
            }
        }

Memory: 8.36 MB
Time: 0.014 s

 

Link и Regex методите леко намаляват performance-a:

    // forth "1"-bits counting algorithm
        counter = binary.Count(x => x == '1');

Memory: 9.20 MB
Time: 0.014 s

 

 // eighth "1"-bits counting algorithm
      string pattern = "1";
      counter = Regex.Matches(binary, pattern).Count;

Memory: 9.29 MB
Time: 0.012 s

 

    // fifth "1"-bits counting algorithm
        counter = binary.Where(x => x == '1').Count();

Memory: 9.65 MB
Time: 0.033 s

Но ,честно казано, разликите са съвсем незначителни и може да се дължат и на натоовареността на judge в момента на тестване.

Преди да сменя BigInteger с ulong, резултата беше Memory: 8.47 MB, Time: 0.052 s - т.е. пак без огромна, тъй като разрешеното време е 0.25 s. Но все пак BigInteger е увеличил 5 пъти времето за изпълнение на програмата - в някои задачи това може да се окаже решаващ фактор.

Основният извод остава, че това преброяване на битивете със стойноост '1' наистина може да се направи по най-различни начини.

2
Filkolev avatar Filkolev 4482 Точки

Най-ефикасният начин за броене на 1-ци битове е това. Цикълът минава толкова итерации, колкото 1-ци има в битовото представяне на числото.

Относно performance, Judge не е надежден измерител. Най-добре да се ползва Stopwatch от System.Diagnostics и всеки метод да се пусне поне няколко десетки хиляди пъти, след което да се сравнят резултатите.

2
11/04/2015 14:41:44
KatyaMarincheva avatar KatyaMarincheva 572 Точки

Виж Филип,

за броя на итерациите в Brian Kernigham's method по принцип си прав - ако работим с малки числа сигурно би дал по-големи разлики. С тестовете на конкретната задача не дава чуствителна разлика:

 while (number != 0)
{
counter++;
number &= number - 1;
}

Memory: 8.12 MB
Time: 0.010 s

        for (int i = 0; i < 64; i++)
        {
            if (1 == (number & 1))
            {
                counter++;
            }
            number = number >> 1;
        }

Memory: 8.21 MB
Time: 0.010 s

Да, judge реагира според колкото е натоварен в момента. Ползвала съм Stopwatch със точно същия успех - най-различни резултати при всяко пускане.

"всеки метод да се пусне поне няколко десетки хиляди пъти, след което да се сравнят резултатите" smile

В предишния си пост твърдя, че BigInteger може да създаде performace проблем не защото веднъж е дал 5 пъти по-дълго време на изпълнение, а от опит - знам колеги които на изпити са губили точки от това че го използват за числа, за които не е необходим.

1
11/04/2015 15:44:35
Filkolev avatar Filkolev 4482 Точки

BigInteger не би трябвало да създава проблем сам по себе си. Това е все едно да ползваш long, когато и int е достатъчно, и заради това да ти гърми програмата. Не е логично. По-скоро нещо друго не е било съобразено в тези случаи.

А относно тестването - да, и Stopwatch-a не е кой знае колко точен, най-добре в такъв случай да се ползва някакъв онлайн инструмент за измервания, но не не ми се е налагало да ползвам и не мога да препоръчам някой конкретен.

0
11/04/2015 16:14:24
Lamms avatar Lamms 197 Точки

Здравейте, сега решавам тази задача, видях и в гуугъл, че има различни начини за преброяване на битовете. Аз като начинаеща, успях да измисля един, който обаче явно е доста тежък.

Можете ли да ми кажете дали задачата така ми е вярна, пък и макар не оптимизирана. В джъджа ми прехвърля времето.

http://pastebin.com/eHUyY6Tk

 

0
Можем ли да използваме бисквитки?
Ние използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Можете да се съгласите с всички или част от тях.
Назад
Функционални
Използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Използваме „сесийни“ бисквитки, за да Ви идентифицираме временно. Те се пазят само по време на активната употреба на услугите ни. След излизане от приложението, затваряне на браузъра или мобилното устройство, данните се трият. Използваме бисквитки, за да предоставим опцията „Запомни Ме“, която Ви позволява да използвате нашите услуги без да предоставяте потребителско име и парола. Допълнително е възможно да използваме бисквитки за да съхраняваме различни малки настройки, като избор на езика, позиции на менюта и персонализирано съдържание. Използваме бисквитки и за измерване на маркетинговите ни усилия.
Рекламни
Използваме бисквитки, за да измерваме маркетинг ефективността ни, броене на посещения, както и за проследяването дали дадено електронно писмо е било отворено.