Եկեք պատկերացնենք, որ աշխարհի բոլոր շենքերի բարձրությունները գրել ենք աճման կարգով: Այսինքն՝ առաջին տարրը աշխարհի ամենացածր շենքն է, իսկ վերջինը՝ ամենաբարձր շենքն է: Այժմ տրված որևէ հարցման (թվի) համար, մենք պետք է գտնենք այդ բարձրությամբ շենքի ինդեքսը ցանկում: Եթե այդ բարձրությամբ շենք չկա, տպում ենք -1:
Փոքր զանգվածների դեպքում կարելի է ամենապարզ մոտեցումը կիրառել՝ for ցիկլով անցնել զանգվածի բոլոր տարրերով և համեմատել հարցման արժեքի հետ: Ամենավատ դեպքում (երբ զանգվածում կա n տարր) այն կկատարի n գործողություն յուրաքանչյուր հարցման համար:
h = [...] # աշխարհի բոլոր շենքերի բարձրությունները աճման կարգով
q = int(input()) # հարցման բարձրությունը
for i, height in enumerate(h):
if height == q: # Եթե գտնում ենք տարրը
print(i) # Տպում ենք ինդեքսը
break # Եվ դադարեցնում ցիկլը
else: # Եթե չգտնենք q բարձրությամբ շենք (else = nobreak)
print(-1) # Տպում ենք -1, ասելով, որ այդպիսի շենք չկա
Սակայն, նկատելով, որ ցուցակը մեծ է և աճող կարգով է տեսակավորված, հասկանում եք, որ բազմաթիվ ավելորդ գործողություններ են արվում: Փոխարենը, կարող ենք ավելի արագ գտնել փնտրվող ինդեքսը:
Քանի որ ամբողջ զանգվածը աճող կարգով է, ավելի օպտիմալ կլինի նայել զանգվածի մեջտեղի տարրին: Եթե այդ տարրը փոքր է q-ից, կարող ենք «հեռացնել» ամբողջ ձախ կեսը և շարունակել փնտրել միայն աջ կեսում: Եթե այդ տարրը մեծ է q-ից, «հեռացնում» ենք աջ կեսը ու կենտրոնանում միայն ձախ մասի վրա: Մեջտեղի տարրը ստուգելու և ավելորդ կեսը «հեռացնելու» գործընթացը հենց Երկուական որոնում (Binary Search) ալգորիթմն է:
h = [...] # աշխարհի բոլոր շենքերի բարձրությունները աճման կարգով
q = int(input()) # հարցման բարձրությունը
l, r = 0, len(h) # Պատասխանը միշտ [l; r) միջակայքում է: Կընդունենք, որ r-ը պատասխանի մաս լինել չի կարող
while r - l > 1: # Առնվազն մեկ տարր միջակայքում դեռ ունենք
mid = (l + r) // 2 # Վերցնում ենք մեջտեղի ինդեքսը
if h[mid] > q: # h[mid] > q => հեռացնում ենք աջ կեսը
r = mid
else: # h[mid] <= q => հեռացնում ենք ձախ կեսը
l = mid
# Արդյունքում h[l]-ը պետք է լինի փնտրվող տարրը
print(l if h[l] == q else -1)
Պատկերացրեք, որ ունենք հետևյալ շենքերի ցուցակը՝ h = [20, 22, 23, 23, 34, 49, 52, 55, 58] և ցանկանում ենք գտնել 49 բարձրությամբ շենքի ինդեքսը:
Այս ալգորիթմով կունենանք մի քանի իտերացիա.
mid = (0 + 9) // 2 = 4 ⇒ h[4] = 34 ≤ 49 ⇒ «հեռացնում» ենք ձախ մասը:
mid = (4 + 9) // 2 = 6 ⇒ h[6] = 52 > 49 ⇒ «հեռացնում» ենք աջ մասը:
mid = (4 + 6) // 2 = 5 ⇒ h[5] = 49 ≤ 49 ⇒ «հեռացնում» ենք ձախ մասը:
l = 5, r = 6 ⇒ r - l == 1 ⇒ while ցիկլը ավարտվում է, և տպում ենք 5, քանի որ h[5] = 49:
Գուցե այս փոքր օրինակով այնքան էլ մեծ արագության տարբերություն չզգացվի, բայց պատկերացրեք, թե ինչ կլիներ, եթե ունենայիք 10 միլիարդ շենք: Եթե հերթով ստուգելու ալգորիթմով գնայինք, ամենավատ դեպքում այն կկատարեր 10 միլիարդ քայլ: Բայց եթե ամեն անգամ զանգվածը բաժանենք երկու մասի ու «հեռացնենք» ավելորդ կեսը, սա հսկայական տարբերություն կտա: Եկեք տեսնենք, քանի գործողություն կպահանջվի 10 միլիարդ տարրը «կիսելով» ամեն անգամ:
Զգուշացում՝ ընդամենը 32 գործողություն 10 միլիարդի փոխարեն
10,000,000,000 / 2 = 5,000,000,000
5,000,000,000 / 2 = 2,500,000,000
1250000000 / 2 = 625000000
625000000 / 2 = 312500000
312500000 / 2 = 156250000
156250000 / 2 = 78125000
78125000 / 2 = 39062500
39062500 / 2 = 19531250
19531250 / 2 = 9765625
9765625 / 2 = 4882812
4882812 / 2 = 2441406
2441406 / 2 = 1220703
1220703 / 2 = 610351
610351 / 2 = 305175
305175 / 2 = 152587
152587 / 2 = 76293
76293 / 2 = 38146
38146 / 2 = 19073
19073 / 2 = 9536
9536 / 2 = 4768
4768 / 2 = 2384
2384 / 2 = 1192
1192 / 2 = 596
596 / 2 = 298
298 / 2 = 149
149 / 2 = 74
74 / 2 = 37
37 / 2 = 18
18 / 2 = 9
9 / 2 = 4
4 / 2 = 2
2 / 2 = 1
Այսպիսով, պատկերացրեք ալգորիթմ, որը գծային որոնմամբ ստուգում է 10 միլիարդ տարրերից յուրաքանչյուրը 1 վայրկյանում: Այդպիսի ալգորիթմը կաշխատեր 317 տարի: Մինչդեռ երկուական որոնումը նույն տվյալների վրա կաշխատեր ընդամենը 32 վայրկյան:
Սա -ի գործողությունների և -ի գործողությունների միջև տարբերությունն է:
Առաջադրանք
Տրված են n աշակերտների գնահատականներ (GPA) 1-ից 4 միջակայքում (1-ն ամենացածրն է, իսկ 4-ը կատարյալը), ինչպես նաև որոշ շեմեր (thresholds): Ունենալով այդ շեմերը՝ անհրաժեշտ է որոշել, թե քանի հոգի կարող է անցնել մյուս դասարան, եթե անցումը կատարվում է այն դեպքում, երբ աշակերտի գնահատականը մեծ կամ հավասար է տրված շեմին:
Մուտք
Մուտքի առաջին տողում տրված է մի ամբողջ թիվ n (1 ≤ n ≤ ) — աշակերտների քանակը:
Հաջորդ տողում տրված են աճման կարգով դասավորված n հատ ռացիոնալ թվեր՝ աշակերտների գնահատականները (1 ≤ ≤ 4):
Երրորդ տողում տրված է մի ամբողջ թիվ q (1 ≤ q ≤ ) — շեմերի քանակը:
Վերջին տողում տրված են q հատ ռացիոնալ թիվ՝ ավարտական շեմերը, որոնցից բարձր կամ հավասար արժեքով գնահատական ունեցողները պետք է անցնեն հաջորդ դասարան:
Ելք
Ծրագիրը պետք է տպի q տող: i-րդ տողում պետք է տպվի այն աշակերտների քանակը, ովքեր անցնում են հաջորդ դասարան շեմի դեպքում: