Autorské riešenie
[stiahni py]                                       

  • Počet riešiteľov: 6 / 6 = 100 %                       

  • Úspešnosť riešenia: 3,1 / 8 = 39 %

Pri tejto úlohe je potrebné si uvedomiť, že úloha nie je vždy riešiteľná. V prípade neriešiteľnosti úlohy je dobré upozorniť používateľa vyhodením výnimky. Pozrime sa na jednotlivé prípady, kedy úloha nemá riešenie a ako riešenie nájsť, ak úloha rieŠenie má.

Ak Jožko doteraz nedostal žiadne známky a ani žiadne už nedostane, tak nie je možné dostať požadovanú výslednú známku.

Ak Jožko doteraz nejaké známky už dostal, ale ďalšie známky už nedostane, tak je vypočítaný priemer z doteraz získaných známok. Tento vypočítaný priemer zaokrúhlime na jednotky. Ak je priemer rovný požadovanej známke, tak je to v poriadku a je vrátený prázdny zoznam. Ak sa priemer nerovná požadovanej známke, tak to znamená, že požadovanú známku nemôže dostať, takže vyhodíme výnimku.

Ak Jožko doteraz nedostal žiadne známky, ale nejaké známky ešte dostane, tak dostane všetky známky rovnakej hodnoty, ako je požadovaná známka.

Aby Jožko mohol dostať požadovanú celkovú známku, tak pre celkový priemer (priemer zo známok ktoré už má a ktoré ešte dostane) musí platiť:

(1) požadovaná známka - 0.5 <= celkový priemer < požadovaná znamka + 0.5

Označme si:

  • z - požadovaná výsledná známka,

  • pocet_m - počet známok, ktoré už dostal,

  • sucet_m - súčet známok, ktoré už dostal,

  • pocet_n - počet známok, ktoré ešte dostane,

  • sucet_n - súčet známok, ktoré ešte dostane.

Z týchto hodnôt si vyjadrime celkový priemer:

(2) celkovy_priemer = (sucet_m + sucet_n) / (pocet_m + pocet_n)

a priemer nových známok:

(3) priemer_nove = (sucet_n / pocet_n)

Ak takto vyjadrené priemery dosadíme do vzťahu (1), môžeme si vyjadriť, aký minimálny a maximálny priemer môžu mať známky, ktoré Jožko ešte dostane:

((z - 0.5) * (pocet_m + pocet_n) - sucet_m) / pocet_n <= priemer_nove

priemer_nove < ((z + 0.5) * (pocet_m + pocet_n) - sucet_m) / pocet_n

Zároveň ale musí platiť: 1 <= priemer_nove <= 5, teda:

((z - 0.5) * (pocet_m + pocet_n) - sucet_m) / pocet_n <= 5

1 < ((z + 0.5) * (pocet_m + pocet_n) - sucet_m) / pocet_n

Tieto vzťahy definujú ďalšie podmienky riešiteľnosti.

Ak pre zadané parametre je úloha riešiteľné, vytvorme zoznam najhorších možných známok. Vytvoríme zoznam nových známok nasledovne:

  1. počet známok bude zodpovedať očakávanému počtu nových známok,

  2. všetky známky budú rovnaké,

  3. ich hodnota bude minimum z hodnôt (5, najhoršia známka lepšia ako horné ohraničenie priemeru nových známok)

  4. postupne budeme každú zo známok zhoršovať o jeden stupeň, ak by zhoršenie známky spôsobilo, že sme dosiahli alebo presiahli hornú hranicu priemeru, zmenu neuskutočníme a končíme, vytvorili sme zoznam najhorších možných známok, pri ktorom Jožko ešte dostane ním určenú najhoršiu známku

Výsledná funkcia nove_znamky() môže vyzerať nasledovne:

#Python
import math


def priemer(znamky):
    ''' Výpočet priemeru známok zo zoznamu.
    :param znamky: zoznam známok, z ktorých je počítaný priemer
    :type znamky: list
    '''
    if len(znamky) == 0:
        return 0
    return sum(znamky) / len(znamky)


def nove_znamky(znamky, pocet_novych_znamok, pozadovana_znamka):
    ''' Zistenie známok, ktoré môže dostať.

    :param znamky: zoznam známok, ktoré už dostal
    :type znamky: list

    :param pocet_novych_znamok: počet známok, ktoré ešte dostane
    :type pocet_novych_znamok: int

    :param pozadovana_znamka: výsledná známka, ktorú chce dostať
    :type pozadovana_znamka: int
    '''

    # Jozko ziadne znamky este nedostal a ani nedostane
    if len(znamky) == 0 and pocet_novych_znamok == 0:
        raise ValueError(
            "Žiadne známky, požadovanú známku nie je možné dosiahnuť.")

    # Jozko ziadne znamky este nedostal ale nejake este dostane
    if len(znamky) == 0:
        return [pozadovana_znamka] * pocet_novych_znamok

    # Jozko uz ziadne znamky nedostane
    if pocet_novych_znamok == 0:
        priemer_starych_znamok = round(priemer(znamky), 0)
        if priemer_starych_znamok == pozadovana_znamka:
            return []
        else:
            raise ValueError("Požadovanú známku nie je možné dosiahnuť.")

    # Jozko nejake znamky dostal a nejake este dostane
    priemer_nove_min = ((pozadovana_znamka - 0.5) *
                        (len(znamky) + pocet_novych_znamok) - sum(znamky)) / \
                       pocet_novych_znamok
    priemer_nove_max = ((pozadovana_znamka + 0.5) *
                        (len(znamky) + pocet_novych_znamok) - sum(znamky)) / \
                       pocet_novych_znamok
    # overime, ci je mozne dostat znamky s pozadovanym priemerom
    if priemer_nove_min > 5:
        raise ValueError("Požadovanú známku nie je možné dosiahnuť.")
    if priemer_nove_max <= 1:
        raise ValueError("Požadovanú známku nie je možné dosiahnuť.")

    nova_znamka = min(math.ceil(priemer_nove_max) - 1, 5)
    # ak by nove znamky boli len 5, nemame uz co zhorsovat
    if nova_znamka == 5:
        return [5] * pocet_novych_znamok

    # postupne zhorsujeme jednotlive znamky
    nove_znamky = [nova_znamka] * pocet_novych_znamok
    for i in range(pocet_novych_znamok):
        nove_znamky[i] += 1
        if priemer(nove_znamky) >= priemer_nove_max:
            nove_znamky[i] -= 1
            break

    return nove_znamky

Funkcia nove_znamky() má 3 parametre:

  • znamky - zoznam známok, ktoré už Jožko dostal

  • pocet_novych_znamok - počet známok, ktoré ešte Jožko dostane

  • pozadovana_znamka - výsledná známka, ktorú chce Jožko dostať.

Táto funkcia vráti zoznam známok, ktoré môže Jožko ešte dostať tak, aby ešte stále dostal požadovanú celkovú známku a rozdiely medzi známkami, ktoré dostane, sú minimálne.

Vaše zaujímavé riešenia a najčastejšie chyby

Najväčším problémom bolo odhalenie prípadov, kedy je úloha neriešiteľná. Prípady neriešitešnosti sú popísané vyššie v popise riešenia.