Loading...
imollov avatar imollov 1 Точки

Въпрос относно последователност от числа в масив

Здравейте,

Решавайки една от задачите в глава 7. Масиви от "Въведение в програмирането със С#" се сблъсках трудност относно разбирането на логикатата й и не успях да я реша. Става въпрос за задача 9. Напишете програма, която намира последователност от числа, чиито сума е максимална. 
Пример: {2, 3, -6, -1, 2, -1, 6, 4, -8, 8} = 11.

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

Да поясня, всички въпроси и коментари, които имам към кода, са с примерните числа от масива в задачата.

int sum = 0, tempSum;
            
            Console.Write("Enter array length: ");
            int length = Int32.Parse(Console.ReadLine());

            int[] arr = new int[length];

            for (int i = 0; i < length; i++)
            {
                Console.Write("Enter {0} element: ", i);
                arr[i] = Int32.Parse(Console.ReadLine());
            }

            for (int i = 0; i < length - 1; i++) // ако вземем числата в условието, то в началото i = 2.
            {
                tempSum = arr[i]; // следователно тук tempSum = 2.

                for (int j = i + 1; j < length; j++) // ако вземем числата в условието, то в началото j = 3.

                {
                    tempSum += arr[j];  // тук tempSum = 2 + 3 = 5.
                    if (tempSum > sum) //тук tempSum е 5, а не 2, ако правилно разсъждавам. 5 > 0, тъй като sum = 0. 
                     {
                     sum = tempSum;     // от тук следва, че sum = 5. 
                     }
                }
            }

            Console.WriteLine("Result is {0}. ", sum);

Ако коментарите са ми верни. То при следващата итерация на вътрешния for loop:

j = -6, tempSum = 2 + (-6) = -4, ако взема стойността на sum от предната итерация то би трябвало да е равен на 5 и няма да се изпълни if-a. 

При следващо завъртане на вътрешния цикъл:

j = -1, tempSum = 2 + (-1) = 1, тук sum коя стойност взема, тази от предната итерация ли, т.е. няма как да го вземе тъй като не се е изпълнил if-a, значи взима от първата и е равен на 5, и пак няма да се изпълни if-a, защото 1 < 5?

Така продължавам до края на вътрешния цикъл, после външния се завърта и вече i = 3,  tempSum = 3. А j = -6, tempSum = 3 + (-6) = -3  и вече не знам sum на какво е равно.

Просто много се обърках. Явно не мисля в правилната посока.

Ако някой може да ми обясни логиката на двата цикъла и как точно се извършват итерациите във вътрешния цикъл, външния и съответно какви са стойностите които приемат tempSum при i, tempSum при j и sum. 

Благодаря предварително.

Тагове:
0
Programming Basics
puffed avatar puffed 289 Точки
Best Answer

Здравей пак,

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

i = 0  =>  arr[i] = 2;    tempSum = 2;  

Влизаме във вътрешния цикъл:

j = i + 1 = 1  =>  arr[j] = 3;    tempSum = 2 + 3 = 5;   5 > 0 =>  sum = 5;  (до тук всичко си го уцелил)

Влизаме във втора итерация на вътрешния цикъл:

j++ => j = 2 =>   arr[j] = -6;    tempSum = 5 + (-6) = -1;  -1 не е > 5 следователно sum не се променя и остава 5;

Влизаме в трета итерация на вътрешния цикъл:

j++ => j = 3 =>   arr[j] = -1;    tempSum = -1 + (-1) = -2;  -2 не е > 5 следователно sum не се променя и остава 5;

и т.н. докато обходи целия масив с j. По този начин се намира сумата на последователността с индекси от 0 до 9

После излизаме от вътрешния цикъл, връщаме се на външния,

i++ => i = 1, arr[i] = 3 , tempSum = 3

j = i + 1 = 2, arr[j] = -6, tempSum = 3 + (-6) = -3, -3 не е по-голямо от 5 (максималната сума)

и т.н.

С дебъгера всичко това се вижда много нагледно. По-добре е sum да се прекръсти на maxSum, за да се знае, че там се пази максималната. 

Реалната последователност с максимална сума е с индекси от 4 до 7. При j = 7, tempSum = 11, тогава sum също става 11. На следващата итерация на j, tempSum вече става 11 + (-8) = 3, sum не се променя. Интересното е, че на последното завъртане в тази поредица tempSum отново става 11 (3 + 8), което е равно на досегашната максимална сума. Т.е. ако гледахме и откъде започва и къде свършва тази въпросна последователност, щеше да трябва да покажем и двете: от индекси 4 до 7 и от 4 до 9. Но ние не пазим позициите, а само максималната сума, затова всичко си е наред. 

Стана малко дълго, но се надявам да е помогнало. Пиши, ако имаш нужда от още обяснения.

0
02/09/2016 02:19:46
imollov avatar imollov 1 Точки

Много благодаря за обясненията.yes

Искам обаче да ти кажа какви са въпросите, на които не си давам еднозначен отговор. smiley

1. Доколкото разбирам още при първото пълно завъртане на вътрешния цикъл се достига маскималната сума 11, при j = 7. И това означава, че при всички следващи завъртания на външния цикъл и вътрешния, маскималната сума няма да превиши 11?

2. При j = 9 от първото завъртане на вътрешния цикъл отново се достига маскималната сума 11, но понеже sum приема стойността на tempSum, само ако tempSum > sum (а sum вече е 11 от завъртането при j = 7), то няма как sum да приеме отново 11. Правилна ли ми е логиката?

3. При условие, че sum приема максималната си стойност (при j = 7) само ако е по-малка от текущата стойност на tempSum, не успях да разбера как тя (стойността на sum) е равна на сбора от стойностите на числата 2, -1, 6 и 4? Освен това, последното което написа: "Т.е. ако гледахме и откъде започва и къде свършва тази въпросна последователност, щеше да трябва да покажем и двете: от индекси 4 до 7 и от 4 до 9. Но ние не пазим позициите, а само максималната сума, затова всичко си е наред. " също не успях да го разбера или може би отговорът е във въпрос № 2, ако правилно съм разсъждавал.

0
puffed avatar puffed 289 Точки

Моля :)

1. Това е така по чиста случайност, защото последните две числа са -8 и 8. Ако последното не беше 8, а 6, нямаше да достигне 11 още при първото завъртане на вътрешния цикъл.

2. Да. sum ще се промени, само ако tempSum > sum.

3. Защото, когато i = 4 и j = 7, тогава в tempSum сме сумирали само числата от arr[4] до arr[7] вкл., които са точно 2, -1, 6, 4 и тогава за първи път попадаме на сума 11, която се явява максималната досега. В този момент sum си я взима и я запазва като най-голяма среана последователна сума. В следващите две итерации на j цикъла, tempSum се намалява веднъж с 8, после се увеличава пак с 8 и отново стига 11, но това няма да промени sum (заради отговора на въпрос 2, да). Така фактически sum продължава да пази само сумата на четирите изброени числа. 

4. А относно онова, което съм написала, не го мисли сега. То искаше да каже, че ако трябва да знаем и къде започва и къде свършва въпросната последователност, щеше да трябва да покажем и двете: 2, -1, 6, 4 и 2, -1, 6, 4, -8, 8, защото и двете дават 11. Но на нас ни трябва само числото 11, затова всичко, което е по-малко или равно на него, не го променя.

Пиши, ако има още нещо.

0
imollov avatar imollov 1 Точки

Мисля, че вече ми се изясниха нещата.

Относно дебъгера,... от това което пишеше в книгата, бях останал с впечатление, че се използва само ако има грешки и искаме да ги открием. Сега обаче го пробвах и наистина виждам логиката съпка по стъпка. smiley

Благодаря ти, отново.

1
puffed avatar puffed 289 Точки

Здравей, 

Преди да погледна искам само да те питам дали пробва да минеш с дебъгера през всяка стъпка?

0
Gesh4o avatar Gesh4o Trainer 305 Точки

Здравей,

Ако трябва да се опише идеята на решението, тя е нещо такова:

1. Минаваме през всеки един елемент от масива.

2. За всеки един елемент събираме последователно числата, намиращи се след него.

3. На всяко едно събиране правим проверка дали текущия сбор е по-голям от вече получения и ако е така - заместваме старата максимална стойност с новата.

Ето пример:

Имаме поредицата 1, 2, 3. Минаваме през първия и елемент(Стъпка №1) и за него последователно събираме всички следващи елементи(Стъпка №2) :

1 = 1 - тук обаче не събираме нищо, тъй като самата стойност може да е максималният сбор.

(Какво става при поредица: -1, -1, 5, -2 ?)

Тук максималния сбор е: 1 (защото е начален - преди него няма други суми). 

1 + 2 = 3

Тук максималния сбор е: 3 (3 >1).

1 + 2 + 3 = 6

Тук максималния сбор е: 6 (6 >3).

На всяка една стъпка правим проверка с текущата максимална сума и ако сегашния сбор е по-голям - той става максималния сбор за момента(Стъпка №3). Съответно за втория елемент имаме:

2 = 2

Тук максималния сбор е: 6 (6 >2)  и т.н.

2 + 3 = 5

И за третия(тук той е последен елемент и затова няма допълнително суми):

3 = 3 

Съгласен съм с предложения от @puffed отговор, като също така мисля, че ако минеш с дебъгера през тази задача ще можеш да разбереш какво се случва и то по-подробно.

Ако има още нещо за доизясняване, пиши. 

Поздрави! :)

1
imollov avatar imollov 1 Точки

Здравей,

След като видях това, което сте ми писали ти и @puffed, и след като дебъгнах кода, нещата малко по малко се подредиха.

Мерси за помощта. yes

0
02/09/2016 21:51:14
kaloyannikov avatar kaloyannikov 531 Точки

Междудругото, ако ти е интересно има доста ефективен алгоритъм(Kadane Algorithm) точно за тоя проблем . Не е може би за основи на програмирането , но всъщност не е толкова труден и като код е доста кратко. А е по-бърз тъй като му отнема N стъпки за да се изпълни, а не N^2.

При редица : -2 , 3 , 2 , -1 

  0 1 2 3
  -2 3 2 -1
currentSum -2 3 5 4
maxSum -2 3 5 5

На всяка стъпка за всеки индекс се записва най-голямата стойност между стойноста на елемента или стойноста на елемента + текущата сума. Другото от алгоритъма е да направиш проверка текущата сума дали е > maxSum и ако е да присвоиш на maxSum стойноста на currentSum.

Като почнем от 1 до дължината  ( отначало слагаме max и current да са array[0] т.е. - 2)  

1ва итерация - currentSum  =  Math.Max(3 , 3 + (-2)) , записва се 3 , maxSum става 3

2ра итерация - currentSum = Math.Max(2 , 3 +2 ) , записва се 5 , maxSum става 5

3та итерация  - currentSum = Math.Max(-1, 5 + (-1)) записва се 4 , maxSum си остава 5 защото 5 > 4 

като цял код - http://pastebin.com/8hHVZ1Sk

3
03/09/2016 12:16:12
Innos avatar Innos 419 Точки

Много съм приятно изненадан от наличието на този отговор тук, ако можех и повече плюсове щях да дам.
Като занимавка за любопитният читател, предлагам да се пробва да модифицира алгоритъмът да връща и началният и крайният индекс на максималната редица.

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