IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Diskuze: Hledání opakujícího čísla v poli a jejich počet.

V předchozím kvízu, Test znalostí C# .NET online, jsme si ověřili nabyté zkušenosti z kurzu.

Aktivity
Avatar
Marek Kaňok
Člen
Avatar
Marek Kaňok:10.3.2022 17:02

Ahoj, jako úplný nováček, který se programováním začal zabývat cca před měsícem prosím o radu, pro vás pravděpodobně trivialního zadání. A taky si chci, tady vyzkoušet přidat příspěvek hehe. Mám za úkol v nelineární posloupnosti čísel najít, zda se tam nějaké čísla opakují. Pokud Ano mam vypsat, které čísla a kolikrát se opakují. Dostal jsem se sem:

Module E13_opakCisel
    '13) Je dána posloupnost kladných celých čísel (ne lineární, tedy čísla budou nějak na přeskáčku).
    'Zjistěte, zda se v dané posloupnosti nějaké číslo opakuje. 13 b) zjistěte, která čísla se opakují.
    '13c) přidejte ještě informaci, kolikrát se opakují.
    Sub mainx()
        Dim radacisel(9) As Byte
        Dim i As Byte, cislo As Byte, n As Byte, op As Byte
        Dim vypis As String, opak As String
        vypis = ""
        Randomize()

        For i = 0 To 9
            cislo = Rnd() * 5 + 1
            radacisel(i) = cislo
            vypis = vypis + Str(radacisel(i)) + ","
        Next

        For i = 0 To 9
            For n = i + 1 To 9
                If radacisel(i) = radacisel(n) Then
                    op = radacisel(n)
                    opak = opak + Str(op) + Chr(10)
                End If
            Next
        Next
        MsgBox(vypis + Chr(10) + "Cisla, ktera se opakuji: " + Chr(10) + opak)
    End Sub
End Module

Takže jsem vlastně zvládl naplnit pole náhodnými čísly (tím jsem vytvořil tu posloupnost). Dále jsem zvládl vypsat všechny čísla, které se opakují. Tady mám ale problém. Pokud číslo je v posloupnosti 3x vypíše vypíše se víckrát viz. obrázek. 4 je v řadě čísel 4x ale vypsala se mi 6x netuším proč. Navíc bych potřeboval, aby mi to vypsalo každé číslo jen jednou: Takže v msgboxu by bylo pouze:
4 (počet kolikrát padlo)
3 (počet kolikrát padlo)
2 (počet kolikrát padlo)
1 (počet kolikrát padlo)

Můžete mě nakopnout jak to mám udělat? Popřípadě mi poradit cestu nebo rovnou napsat code? :-D
Díky moc za váš čas

 
Odpovědět
10.3.2022 17:02
Avatar
HONZ4
Člen
Avatar
Odpovídá na Marek Kaňok
HONZ4:10.3.2022 21:40

Vykašli na Visual Basic, je prakticky K.O. Je to ztráta času. Uč se raději C#.

Musíš si udělat pole pro každou hodnotu (1,2,3,4) a tam navyšovat hodnotu, která padla.

 
Nahoru Odpovědět
10.3.2022 21:40
Avatar
zelvicek
Člen
Avatar
Odpovídá na Marek Kaňok
zelvicek:11.3.2022 7:26
  1. podpořil bych @Honz4 ve výběru jazyka. Pokud jen ten VB.NET vyžadován nějakým učitelem/lektorem, vyfackuj ho. Násilí se nemá podporovat, ale v tomto případě je na místě.
  2. doporučuju si udělat danou funkcionalitu v reálu. Napiš si pár čísel na papír a navrhni algoritmus, kterým docílíš požadovaného výsledku.

A jako bonus - C# řešení, které tě nenaučí probírané téma (smyčky a práce s poli):

Random rand = new Random();
(int Value, int Count)[] res = Enumerable.Range(0, 100).Select(x => rand.Next(0, 50)).ToArray()
  .GroupBy(x => x).Select(x => (Value: x.Key, Count: x.Count())).Where(x => x.Count > 1).OrderBy(x => x.Value).ToArray();
 
Nahoru Odpovědět
11.3.2022 7:26
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:11.3.2022 8:02

Proc tak slozite?

Prvni cast dela random a ulozi hodnotu cisla to do pole. OK.

Druha cast muze vytvaret pole. `if exist(pole[hod­nota]) pole[hodnota]++ nebo pole[hodnota]=1 `. A pak potrebujes dalsi cyklus, ktery vypisuje vsechny hodnoty, ktere nejsou 1.
Ty to resis nejak strasne slozitou cestou, ze pokazde prohledavas cele pole (takovy jako kdyz bubble sort). Kdybys mel milion cisel, tak to pocitas jeste druhy den :)

Akceptované řešení
+20 Zkušeností
+2,50 Kč
Řešení problému
 
Nahoru Odpovědět
11.3.2022 8:02
Avatar
Odpovídá na Marek Kaňok
Matúš Olejník:11.3.2022 11:29

Presne ako spomenul Peter, číslo ktoré padne môžeš rovno použiť ako index do poľa kde zvýšiš hodnotu o 1. Takto sa robí aj histogram pre výskyt písmen atď.

Module VBModule
    '13) Je dána posloupnost kladných celých čísel (ne lineární, tedy čísla budou nějak na přeskáčku).
    'Zjistěte, zda se v dané posloupnosti nějaké číslo opakuje.
    '13 b) zjistěte, která čísla se opakují.
    '13c) přidejte ještě informaci, kolikrát se opakují.
    Sub Main()
        Dim randomIntervalFrom, randomIntervalTo, i, randomNumber, randomNumbersCount As Integer
        Dim histogram() As Integer
        Dim output As String
        Dim someNumberIsRepeated As Boolean

        output = ""
        someNumberIsRepeated = False

        'toto keby si chcel niekedy dynamicke velkosti
        randomNumbersCount = 20
        randomIntervalFrom = 1
        randomIntervalTo = 20
        ReDim histogram(randomIntervalTo + 1)

        Randomize()

        For i = 0 To randomNumbersCount - 1
            randomNumber = Int((randomIntervalTo - randomIntervalFrom + 1) * Rnd + randomIntervalFrom)
            histogram(randomNumber) = histogram(randomNumber) + 1
            If histogram(randomNumber) > 1 Then
                someNumberIsRepeated = True
            End If

            Console.Write(Str(randomNumber) + " ")
            output = output + Str(randomNumber) + ", "
        Next

        Console.WriteLine

        If someNumberIsRepeated Then
            Console.Write("Existuje cislo/cisla ktore sa opakuje/opakuju")
            Console.WriteLine

            For i = randomIntervalFrom To randomIntervalTo + 1
                If histogram(i) > 1 Then
                    Console.Write("Cislo " + Str(i) + " sa opakuje " + Str(histogram(i)) + " krat ")
                    Console.WriteLine
                End If
            Next
        Else
            Console.Write("Ziadne cislo sa neopakuje")
        End If
    End Sub
End Module

Do MsgBoxu už si to prerobíš ;)

Nahoru Odpovědět
11.3.2022 11:29
/* I am not sure why this works but it fixes the problem */
Avatar
Jakub Raida
Tvůrce
Avatar
Jakub Raida:11.3.2022 14:17

Řešení v C#. Jistě už se dá snadno si tam domyslet např. seřazení apod. (a není tam samozřejmě XAML a obslužná třída):

public class NajiteCislo
    {
        public int NumerickaHodnota { get; private set; }
        public int Vyskytovost { get; private set; }

        public NajiteCislo(int numerickaHodnota, int vyskyty)
        {
            NumerickaHodnota = numerickaHodnota;
            Vyskytovost = vyskyty;
        }
    }

public class OpakoC
    {
        public int Pocitadlo { get; private set; }
        public int PocetCisel { get; private set; }
        public string Vypis { get; private set; }
        public Random Nahoda { get; private set; }
        public List<int> Cisla { get; private set; }
        public List<NajiteCislo> NajitaCisla { get; private set; }

        public OpakoC(int pocetCisel)
        {
            PocetCisel = pocetCisel;
            Nahoda = new Random();
        }

        public string VypisOpakujiciCisla()
        {
            // Nové listy
            Cisla = new List<int>();
            NajitaCisla = new List<NajiteCislo>();

            // Vytvoříme si náhodnou řadu čísel
            for (int i = 0; i < PocetCisel; i++)
            {
                Cisla.Add(Nahoda.Next(1, 20));
            }

            // Procházíme řadu čísel
            foreach (int prvniVyskyt in Cisla)
            {
                Pocitadlo = 0;

                foreach (int vsechnyVyskyty in Cisla)
                {
                    if (prvniVyskyt == vsechnyVyskyty)
                    {
                        Pocitadlo++;
                    }
                }

                NajitaCisla.Add(new NajiteCislo(prvniVyskyt, Pocitadlo));
            }

            // Vyprázdníme si výpis
            Vypis = String.Empty;

            // Vytvoříme si výpis
            foreach (NajiteCislo polozka in NajitaCisla)
            {
                Vypis += String.Format("Číslo: {0} ---- Počet výskytů: {1}\n", polozka.NumerickaHodnota, polozka.Vyskytovost);
            }

            // Vrátíme výpis
            return String.Format(Vypis);
        }
    }
 
Nahoru Odpovědět
11.3.2022 14:17
Avatar
Jakub Raida
Tvůrce
Avatar
Jakub Raida:11.3.2022 14:29

Myšlenka je tam ta, že foreachem procházím všechna čísla a u každého přitom dalším vnořeným foreachem srovnávám toto číslo se všemi ostatními. Když najdu shodu (a to včetně shody samotného se sebou), tak posunu počítadlo. Na závěr uložím údaj, ve kterém je numerická hodnota čísla a počet jeho výskytů (počítadlo).

 
Nahoru Odpovědět
11.3.2022 14:29
Avatar
DarkCoder
Člen
Avatar
DarkCoder:11.3.2022 14:58

Tato úloha ač je primitivní je zajímavá v tom, že je zde velký prostor pro optimalizaci. Respektive ji lze napsat jak efektivně, tak velmi neefektivně. Celé to způsobuje informace, zda existuje číslo, které se opakuje, či nikoli. Nebudu se zde zabývat způsobem, který už tu padl, tedy variantou, kde se prohledává zbytek pole a hledá se ta samá hodnota. Toto řešení je velice neefektivní. Ale budu psát o způsobu, kde se aktualizuje hodnota na daném indexu, čímž vzniká informace o počtu konkrétního čísla v poli.

Efektivita zápisu je závislá na dvou hlavních veličinách. A to na počtu hodů a rozsahu, jakých hodnot mohou náhodná čísla nabývat.

Testovat existenci opakování čísla v poli (hodnota na indexu > 1) v cyklu generující náhodná čísla je pohodlné, ale provádí se nad všemi prvky pole. Tuto operaci nelze přerušit, neboť generování čísel je třeba dokončit. Skvěle se hodí, je-li počet hodů podstatně menší nežli rozsah čísel a to i přesto, že indikace opakování se přepisuje. Bude-li počet hodů blízký rozsahu čísel nikoli však větší, je lepší testovat existenci opakování čísla už mimo cyklus generování čísel, neboť lze tuto operaci předčasně ukončit a ušetřit tak drahocenný čas. A také pokračovat ve výpisu tam, kde jsme při testu prvního výskytu opakování skončili. Pokud bude počet hodů větší než rozsah čísel, pak je test existence opakování čísla naprosto zbytečný, neboť vždy existuje číslo které se opakuje.

Následující kód ukazuje optimalizaci pro případ, kdy počet hodů není v porovnání s rozsahem čísel podstatně menší a ne větší než rozsah (PH <= RC).

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define LRANGE 1
#define RRANGE 6
#define THROWS 5

int main(void) {
        int vals[RRANGE + 1] = {0};
        int reppos = 0;

        srand((unsigned)time(NULL));

        // naplneni pole nahodne vygenerovanymi cisly v rozsahu
        for (int i = 0, k = RRANGE - LRANGE + 1; i < THROWS; i++) vals[(rand() % k) + LRANGE]++;

        // test existence opakujiciho se cisla, prvni vyskyt
        for (int i = LRANGE; i <= RRANGE; i++) {
                if (vals[i] > 1) {
                        reppos = i;
                        break;
                }
        }

        // Vypis informaci o existenci opakujicich se cisel
        if(!reppos) puts("Neexistuje cislo ktere by se opakovalo");
        else {
                puts("Existuje cislo ktere se opakuje");
                for (int i = reppos; i <= RRANGE; i++) {
                        if (vals[i] > 1) printf("Cislo %d se opakuje %dx\n", i, vals[i]);
                }
        }

        return 0;
}

Výhodou tohoto způsobu je, že test existence opakujícího se čísla se nad každým indexem provádí právě a pouze jednou. Testování se ukončí s prvním výskytem opakujícího se čísla a od indexu tohoto výskytu pokračuje zbylé vypisování informací. Se vzrůstajícím poměrem rozsahu čísel ku počtu hodů efektivita tohoto způsobu klesá.

Existence opakujícího se čísla lze tedy provádět u počtu hodů nebo rozsahu čísel. Na základě poměru zvolit vhodný způsob.

Výpis informací opakování se čísla se provádí u rozsahu čísel. S možným ubývajícím množstvím opakujících se čísel lze přemýšlet nad optimalizací (neprocházet celé pole kvůli pár opakujícím se číslům, zejména pokud rozsah bude extrémně velký a možný počet opakujících se čísel minimální).

Tedy:

PH >> RC - netřeba provádět existenci opakování, vždy se nějaké číslo opakuje, možný vysoký výskyt opakování
PH > RC - netřeba provádět existenci opakování, vždy se nějaké číslo opakuje, počet opakování nižší, nabízí se otázka optimalizace při závěrečném výpisu.
PH <= RC - existenci opakování provádět vně cyklu generující náhodná čísla, počet opakování ještě nižší, optimalizace při závěrečném výpisu rozhodně vhodná
PH << RC - existenci opakování provádět uvnitř cyklu generující náhodná čísla, počet opakování minimální, optimalizace při závěrečném výpisu téměř či úplně nutná.

Kde:
PH - počet hodů
RC - rozsah čísel

Nahoru Odpovědět
11.3.2022 14:58
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
Fodo
Člen
Avatar
Fodo:16.3.2022 19:07

Spravil som pokus. Vytvoril som 4 verzie programu. Pre zoznam čísiel používam List. Krátky popis:
Verzia 1:
zoradí List a potom stačí keď prejde celé pole iba jeden krát

Verzia 2:
prechádza List v dvoch cykloch, v prípade že nájde zhodu, zaznamená to a položku z Listu odstráni, čím redukuje počet iterácii cyklu

Verzia 3:
Od používateľa Jakub Raida
Nevýhodou je výpis - všetky duplicitné čísla vypisuje tzn. napr. ak je číslo 9 v poli 5x tak ho 5x vypíše

Verzia 4:
Od používateľa zelvicek
toto naštudované nemám :)

Tu je vysledok testu: Trvanie je v milisekundách
Počet čísiel: 10
Verzia 1 trvanie: 1,7476
Verzia 2 trvanie: 1,0081
Verzia 3 trvanie: 2,9303
Verzia 4 trvanie: 71,8895

Počet čísiel: 100
Verzia 1 trvanie: 4,7877
Verzia 2 trvanie: 2,9706
Verzia 3 trvanie: 25,2603
Verzia 4 trvanie: 85,0549

Počet čísiel: 1 000
Verzia 1 trvanie: 3,301
Verzia 2 trvanie: 10,0022
Verzia 3 trvanie: 240,0204
Verzia 4 trvanie: 133,0393

Počet čísiel: 10 000
Verzia 1 trvanie: 5,0046
Verzia 2 trvanie: 15,0028
Verzia 3 trvanie: 4649,8514
Verzia 4 trvanie: 76,2385

Počet čísiel: 100 000
Verzia 1 trvanie: 15,8993
Verzia 2 trvanie: 1187,2465
Verzia 3 trvanie: 451568,8017
Verzia 4 trvanie: 86,0043

using System;
using System.Collections.Generic;
using System.Linq;

namespace OpakovaniaCislaVpoli
{
        internal class Program
        {
                List<int> list = new List<int>();
                string opakujuceCisla = "";

                public List<NajiteCislo> NajitaCisla { get; private set; }
                public int Pocitadlo { get; private set; }
                public int PocetCisel { get; private set; }
                public string Vypis { get; private set; }
                public Random Nahoda { get; private set; }

                int min = 1;            // minimálna hodnota čísiel
                int max = 20;           // (max - 1) - maximálna hodnota čísiel
                int pocet = 100000;     // počet

                static void Main()
                {
                        Program program = new Program();

                        program.NacitajHodnoty();

                        program.HladajDuplicity1();
                        program.HladajDuplicity3();
                        program.HladajDuplicity4();
                        program.HladajDuplicity2();

                        Console.ReadKey();
                }

                void NacitajHodnoty()       // Naplní List náhodnými číslami
                {
                        Random rand = new Random();
                        for (int i = 0; i < pocet; i++)
                                list.Add(rand.Next(min, max));
                }

                private void HladajDuplicity1()     // zoradí List a potom postupne hľadá počet opakovaní
                {
                        opakujuceCisla = "";
                        int pocetOpakovani = 1;
                        list.Sort();
                        for (int i = 0; i < list.Count - 1; i++)
                                if (list[i] == list[i + 1])
                                        pocetOpakovani++;
                                else if (pocetOpakovani > 1)
                                {
                                        opakujuceCisla += $"Cislo {list[i]} sa nachádza v poli {pocetOpakovani }x\n";
                                        pocetOpakovani = 1;
                                }
                                else
                                        pocetOpakovani = 1;

                        if (pocetOpakovani > 1)
                                opakujuceCisla += $"Cislo {list[list.Count - 1]} sa nachádza v poli {pocetOpakovani }x\n";

                        // Výpis hodnôt
                        Console.WriteLine($"Verzia1: \n{opakujuceCisla}");
                }

                private void HladajDuplicity2()
                {
                        opakujuceCisla = "";
                        int pocetOpakovani = 1;
                        for (int i = 0; i < list.Count-1; i++)
                        {
                                for (int j = i+1; j < list.Count; j++)
                                        if (list[i] == list[j])
                                        {
                                                pocetOpakovani++;
                                                list.RemoveAt(j);
                                                j--;
                                        }
                                if (pocetOpakovani > 1)
                                {
                                        opakujuceCisla += $"Cislo {list[i]} sa nachádza v poli {pocetOpakovani }x\n";
                                        pocetOpakovani = 1;
                                }
                        }

                        //Výpis
                        Console.WriteLine($"Verzia2: \n{opakujuceCisla}");
                }

                private void HladajDuplicity3() // Jakub Raida
                {

                        NajitaCisla = new List<NajiteCislo>();

                        // Procházíme řadu čísel
                        foreach (int prvniVyskyt in list)
                        {
                                Pocitadlo = 0;

                                foreach (int vsechnyVyskyty in list)
                                        if (prvniVyskyt == vsechnyVyskyty)
                                                Pocitadlo++;

                                NajitaCisla.Add(new NajiteCislo(prvniVyskyt, Pocitadlo));
                        }

                        // Vyprázdníme si výpis
                        Vypis = String.Empty;

                        // Vytvoříme si výpis
                        foreach (NajiteCislo polozka in NajitaCisla)
                                Vypis += String.Format("Číslo: {0} ---- Počet výskytů: {1}\n", polozka.NumerickaHodnota, polozka.Vyskytovost);

                        //Výpis
                        Console.WriteLine($"Verzia3: \n{Vypis}");
                }

                void HladajDuplicity4() // zelvicek
                {
                        Random rand = new Random();
                        (int Value, int Count)[] res = Enumerable.Range(0, pocet).Select(x => rand.Next(min, max)).ToArray()
                          .GroupBy(x => x).Select(x => (Value: x.Key, Count: x.Count())).Where(x => x.Count > 1).OrderBy(x => x.Value).ToArray();
                        for (int i = 0; i < res.Length; i++)
                                Console.WriteLine($"Číslo {res[i].Value}  sa nachádza v poli {res[i].Count}x");
                }
        }
        public class NajiteCislo
        {
                public int NumerickaHodnota { get; private set; }
                public int Vyskytovost { get; private set; }

                public NajiteCislo(int numerickaHodnota, int vyskyty)
                {
                        NumerickaHodnota = numerickaHodnota;
                        Vyskytovost = vyskyty;
                }
        }
}
 
Nahoru Odpovědět
16.3.2022 19:07
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:17.3.2022 8:19

No, a co reseni, ze to zapisujes do pole?

1. pole1 = random cisla
2. existuje(pole2[ pole1[i] ]) ? pole2[ pole1[i] ]+1 : 1
3. vypis

Ps, ja jsem myslel, ze C# nebo C++ umi veci delat rychle. Ja si tady jako serazuji milion cisel do par sekud a ty tam neco pocitas stovky? Pozor, u slabsich algoritmu mam stopku pri 0.5s.
https://mlich.zam.slu.cz/…g4-pokus.htm

To od Zelvicek je jen jiny zapis s vyuzitim vestavenych funkci.

// naplneni pole random cisly, tusim value=0-50, pocet cisel 100
Random rand = new Random();
(int Value, int Count)[] res = Enumerable.Range(0, 100).Select(x => rand.Next(0, 50)).ToArray()

// pro kazde x, vyber x a preved to na pole [ Value, Count:]
.GroupBy(x => x).Select(x => (Value: x.Key, Count: x.Count())).

// a vyber z toho pole vse , kde count>1
// a serad podle x (slo by tam seradit podle count)
Where(x => x.Count > 1).OrderBy(x => x.Value).ToArray();

Coz je vlastne totez, co sem psal ja. Jen, cyklem by to bylo asi rychlejsi, protoze ty specialni funkce maji slozitejsi kod.

 
Nahoru Odpovědět
17.3.2022 8:19
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:17.3.2022 8:23

Jestli mu to chces urychlit, tak z posledni casti vyrat to orderovani

Where(x => x.Count > 1).ToArray();
 
Nahoru Odpovědět
17.3.2022 8:23
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:17.3.2022 8:29

Jo, a mimochodem, mozna to bude znit hloupe, protoze c# neznam, ale

  • kdyz provedes u listu sort a on je globalni promena, tak, nezmeni se to i pro ty dalsi?
  • a podobne s remove
  • a u zelvicka ti to brzdi ta random cast, jeste, kterou ti ostatni delat nemusi (to se da zohlednit spocitanim sumy pro random a odecist od zelvickove sumy)
 
Nahoru Odpovědět
17.3.2022 8:29
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:17.3.2022 8:34

Jo, a duplicity 2,3 ti brzdi jeste tento radek

for (int j = i+1; j < list.Count; j++)

Funguje to tak, ze on v kazdem cyklu vyhodnocuje podminku a list.Count pocita znovu a znovu. Protoze count neni konstanta.
Takto bys mohl ziskat tak spoustu casu, vic procent, cim delsi je array.

int j_end = list.Count;
for (int j = i+1; j < j_end; j++)
 
Nahoru Odpovědět
17.3.2022 8:34
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Fodo
DarkCoder:17.3.2022 11:26

Porovnej výsledky s tímto:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>

#define LRANGE 1
#define RRANGE 20
#define THROWS 100000

double initPerformanceCounter(void);
__int64 getTicks(void);

int main(void) {
        int vals[RRANGE + 1] = { 0 };
        double PCFreq;
        __int64 tick1, tick2, tick3;

        srand((unsigned)time(NULL));

        PCFreq = initPerformanceCounter();
        if (!PCFreq) {
                fprintf(stderr, "Inicializace Performance counteru selhala\n");
                exit(1);
        }

        tick1 = getTicks();

        for (int i = 0, k = RRANGE - LRANGE + 1; i < THROWS; i++) (*(vals + (rand() % k) + LRANGE))++;

        tick2 = getTicks();

        for (int *p = vals + LRANGE, *p2 = vals + RRANGE, *p3 = p; p <= p2; p++) {
                if (*p > 1) printf("Cislo %d se opakuje %dx\n", p - p3 + LRANGE, *p);
        }

        tick3 = getTicks();

        putchar('\n');
        printf("Pocet generovanych cisel: %d\n", THROWS);
        printf("Rozsah generovanych cisel: %d-%d\n", LRANGE, RRANGE);
        printf("Doba generovani histogramu: %f ms\n", (double)(tick2 - tick1) / PCFreq);
        printf("Doba vypisu opakujicich se cisel: %f ms\n", (double)(tick3 - tick2) / PCFreq);
        printf("Celkova doba (histogram + vypis): %f ms\n", (double)(tick3 - tick1) / PCFreq);

        return 0;
}

double initPerformanceCounter(void) {
        LARGE_INTEGER li;
        if (!QueryPerformanceFrequency(&li)) return 0.0;
        return (double)(li.QuadPart) / 1000.0;
}

__int64 getTicks(void) {
        LARGE_INTEGER li;
        if (!QueryPerformanceCounter(&li)) return 0;
        return li.QuadPart;
}
Editováno 17.3.2022 11:28
Nahoru Odpovědět
17.3.2022 11:26
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
Fodo
Člen
Avatar
Odpovídá na Peter Mlich
Fodo:18.3.2022 13:33

Ak by som chcel do poľa zapisovať musím poznať jeho veľkosť, pri List nepotrebujem, a verzia s odstraňovaním prvkov by mi tiež nefungovala tak ako chcem.

Tie časy sú v milisekundách

Ďakujem za vysvetlenie kódu od Zelvicek

 
Nahoru Odpovědět
18.3.2022 13:33
Avatar
Fodo
Člen
Avatar
Odpovídá na Peter Mlich
Fodo:18.3.2022 13:36
  • máš pravdu zmení, preto som tuto verziu použil ako poslednú
  • detto
  • áno máš pravdu, musím to prerobiť aby to bolo objektívne :)
 
Nahoru Odpovědět
18.3.2022 13:36
Avatar
Fodo
Člen
Avatar
Odpovídá na Peter Mlich
Fodo:18.3.2022 13:40

Myslel si Duplicity 1 a 2 - to som si neuvedomil, ale použiť to môžem iba pri Duplicite1, pretože pri Duplicite2 sa mi mení veľkosť Listu

 
Nahoru Odpovědět
18.3.2022 13:40
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Fodo
DarkCoder:18.3.2022 15:28

Nejsem C# programátor, nikdy jsem v něm nic nenapsal.
Níže přikládám přepis mého způsobu uvedeného dříve z C do C#.
Jaké budou výsledky zde?

using System;
public class Program {
    public static void Main() {
        int min = 1;
        int max = 20;
        int throws = 100000;

        int[] vals = new int[max + 1];

        // generovani histogramu
        Random rand = new Random();
        for (int i = 0; i < throws; i++) vals[rand.Next(min, max + 1)]++;

        // vypis opakujicich se cisel
        for (int i = min; i <= max; i++){
            if(vals[i] > 1) Console.WriteLine("Cislo {0} se opakuje {1}x", i, vals[i]);
        }
    }
}
Nahoru Odpovědět
18.3.2022 15:28
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:18.3.2022 15:49

Poradi mas 1 3 4 2. Sort delas v 1. Tim padem plati i pro 3 4 2. A muze ti to znacne zkreslit vysledek. Treba slabinou bubble-quick-sort je pole serazene pozpatku. Slabinou insert-sortu take. Pokud to seradis, tak nemas jistotu, zda to neovlivni rychlost. Pracuji tedy s rozdilnym vstupem a tudiz ty casy nelze vzajemne porovnat.
A take pracuji s rozdilnym vystupem. Docilit shodneho vystupu muze ten algoritmus znacne zatizit.
A take u mereni musis zvazit vliv okolni zateze. Pocitac ti neda vykon, kdyz neco dela na pozadi.

Ale, jako, v zasade z kodu a cisel je jasne, ze tam rozdil je.

Sort a secteni je vyborny napad (Dupl1).

Odstranovani prvku (Dupl2), to je pro velke pole pomale. Kazdopadne to mas sortnute a timpadem to neni treba odstranovat. Kazda operace navic zere cas. Predevsim IF a odstanovani, pokud neni z konce pole nebo ze zacatku.

if (list[i] == list[j]) // pokud toto nastane, tak je pocet opakovani minimalne 2
                   {
                           pocetOpakovani++;
                           list.RemoveAt(j);
                           j--;
                   }
           if (pocetOpakovani > 1) // tudiz nema smysl se znovu ifem ptat
           {

(Dupl3) opakovane prochazi totez pole, dostava se do zatizeni n * n.
(Dupl4)

A tak jako celkove je rychlejsi a uspornejsi pracovat s array. List je mozna elegantni, ale prace s array by mela byt rychlejsi, pokud ti zalezi na vykonu. Tipnul bych to o 10-20%, podle app.

 
Nahoru Odpovědět
18.3.2022 15:49
Avatar
Fodo
Člen
Avatar
Odpovídá na Peter Mlich
Fodo:18.3.2022 22:58

S tým Sort máš pravdu, to ovplyvnilo výsledok, som si to neuvedomil, prerobil som to:
Varianta 1 - zmenil som list.Count na konštantu, trochu to zrýchlilo ale moc nie, pri 100k číslach to bolo rýchlejšie o cca 2 ms (21,8550125 ms - 19,855275 ms), spravil som aj pokus s polom, ale to trvalo raz tak dlho, cez 40 ms
Varianta 2 - nechal som vytvoriť nezoradené pole a to už bolo na čase cítiť - 559,69853125 ms pri tom istom teste ako vyššie
Varianta 3 - som vypustil
Varianta 4 - to som nechal ako bolo a čas 48,9144375 ms

 
Nahoru Odpovědět
18.3.2022 22:58
Avatar
Fodo
Člen
Avatar
Odpovídá na DarkCoder
Fodo:18.3.2022 23:01

Toto sa nedá porovnávať, pretože v mojich príkladoch máš polia o 100 000 prvkoch a ty máš pole o 20 prvkoch, ale hodil som to do programu a čas mi vyšiel - 36,43126875 ms

 
Nahoru Odpovědět
18.3.2022 23:01
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Fodo
DarkCoder:19.3.2022 2:03

Samozřejmě že se to porovnávat dá, zadání úlohy je pro všechny testy stále stejné: Vygeneruj 100000 náhodných čísel v rozsahu 1-20 a vypiš na obrazovku hlášku o tom, kolikrát se opakované číslo opakuje. Jak už se s tím který způsob popere, je jedno.

Jako výchozí hodnotu budu brát čas nejrychlejší verze, tedy té první kde se využívá List, a to: 15,8993 ms.

Verze s polem, jehož hodnoty na indexech představují počty opakování čísla odpovídající indexu a výpisem pole o velikosti rozsahu generovaných náhodných čísel (1-20) s lineární složitostí na základě jedné relace, má čas: 36,4313 ms.

Nevěřím, že by tato verze s polem byla více než 2x pomalejší, než první verze s Listem, ve které se provádí tolik úkonů.

A tak uděláme test přímo v programu nad jednotlivými verzemi a uvidíme, jak to skutečně je. :-)

Pro zjištění času použijeme třídu Stopwatch.

using System.Diagnostics;
// ...

Stopwatch sw = new Stopwatch();
sw.Start();

// mereny usek programu

sw.Stop();

Console.WriteLine("cas = {0}", sw.Elapsed);

Tedy verze s polem doplněná o kód potřebný k měření času úseku kódu:

using System;
using System.Diagnostics;
public class Program {
    public static void Main() {
        int min = 1;
        int max = 20;
        int throws = 100000;

        int[] vals = new int[max + 1];

        Stopwatch sw1 = new Stopwatch();
        sw1.Start();

        // generovani histogramu
        Random rand = new Random();
        for (int i = 0; i < throws; i++) vals[rand.Next(min, max + 1)]++;

        sw1.Stop();

        Stopwatch sw2 = new Stopwatch();
        sw2.Start();

        // vypis opakujicich se cisel
        for (int i = min; i <= max; i++){
            if(vals[i] > 1) Console.WriteLine("Cislo {0} se opakuje {1}x", i, vals[i]);
        }

        sw2.Stop();

        Console.WriteLine("sw1 = {0}", sw1.Elapsed);
        Console.WriteLine("sw2 = {0}", sw2.Elapsed);
    }
}

U ostatních verzí tedy udělat totéž (nezapomenout na using System.Diagnos­tics):

using System.Diagnostics;
// ...

static void Main() {
        Program program = new Program();

        Stopwatch sw1 = new Stopwatch();
        sw1.Start();

        program.NacitajHodnoty();

        sw1.Stop();

        Stopwatch sw2 = new Stopwatch();
        sw2.Start();

        program.HladajDuplicity1();
        // program.HladajDuplicity3();
        // program.HladajDuplicity4();
        // program.HladajDuplicity2();

        sw2.Stop();

        Console.WriteLine("sw1 = {0}", sw1.Elapsed);
        Console.WriteLine("sw2 = {0}", sw2.Elapsed);

        Console.ReadKey();
}

Zajímají mě výpisy naměřených hodnot sw1 a sw2 všech verzí. Nad každou verzí spustit program alespoň 5x. :-)

Ještě jeden dodatek k funkci generující náhodné číslo.

int min = 1;
int max = 20;
// ...

Random rand = new Random();
for (int i = 0; i < pocet; i++) list.Add(rand.Next(min, max));

rand.Next(min, max) způsobí vygenerování náhodného čísla v rozsahu 1-19, nikoli v rozsahu 1-20.

Je tedy třeba upravit zdrojový kód na rand.Next(min, max + 1)

Nahoru Odpovědět
19.3.2022 2:03
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:19.3.2022 23:01
int max = 20;
rand.Next(min, max + 1)

// lepe asi zvetsit predem nez to pokazde scitat az v Next()
int max = 20;
max++;
rand.Next(min, max)

DarkCoder - C neovladam, ale, vim, ze spousta jazyku nama rada, kdyz je promena undefined a tvuj historogram zvysuje promenou ++, ktera jeste nebyla vytvorena, neobsahuje nulu nebo ma dokonce nahodnou hodnotu. To by mohlo cely proces brzdit. Ale treba to funguje jinak, treba, kdyz mas pole int, tak je tam nula jakoby od zacatku.

vals[rand.Next(min, max + 1)]++
 
Nahoru Odpovědět
19.3.2022 23:01
Avatar
Peter Mlich
Člen
Avatar
Odpovídá na Fodo
Peter Mlich:19.3.2022 23:20

ups, ja jsem ti dal spatny link, na pokusnou verzi. tam ted testuji jeden algoritmus, tak to mam omezene na 15 prvku
https://mlich.zam.slu.cz/…g4-pokus.htm

  • dej si test 2 (nebo 3), klikni 100.000 prvku, klikni na "All" u algoritmu a klikni "start"
  • vsude, kde se neobjevi t0, tak byl algoritmus stopnut pro cas>500ms (to sem tam dal zamerne stopku, kdyby nekdo pustil algoritmus, co by to pocital hodinu :) )
  • u mne trvalo 45ms nez se provedl tim-sort pole o velikosti 100.000 timsortem, a jednalo se o 3.397.537 cyklu, coz je 34x vic nez tvych 100.000 cyklu. Takze, tvuj kod by mel byt kolem 2-5-10 ms.
  • a taky tam mas counting sort, ktery to seradil za polovicni cas, pri pouziti 300.000 cyklu (vytvari nejdriv pole pocet opakovani, to resis ty, + pak z nej generuje serazene pole - tento algoritmus se pouziva pro serazeni pole s integer hodnotami); cili, mel bys byt rychlejsi nez counting-sort Pigeonhole (ale mam pomaly pocitac, tak, treb na tvem to bude 2ms a ne 20ms)

Pokud tam tedy mas 1000 a vic, tak je neco zasadne spatne. A take je jasne, u zelvicka, ze dela nejspis nejake operace navic, kdyz nedosahl tech 15ms jako ten prvni kod.

 
Nahoru Odpovědět
19.3.2022 23:20
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Peter Mlich
DarkCoder:19.3.2022 23:29

Pokud bychom se bavili o C, pak globální proměnné a statické proměnné jsou inicializovány na 0. Co se týká lokálních proměnných, tam proměnná skutečně obsahuje náhodnou hodnotu.

Co se týká C#, tak defaultní hodnota pro numerické typy je 0. Alespoň co jsem se v rychlosti dočetl. To byl důvod, proč jsem deklaroval pole, ale neprovedl jsem nastavení jeho prvků na 0. Spolehl jsem se na defaultní 0 hodnotu a tu jsem si pak při testování ještě ověřil. A skutečně to tak je.

V C bych musel udělat:

int vals[RRANGE + 1] = { 0 };

Tím inicializuji všechny prvky pole vals na hodnotu 0.

Co se týká generování histogramu

// generovani histogramu
Random rand = new Random();
for (int i = 0; i < throws; i++) vals[rand.Next(min, max + 1)]++;

Ano, dobrý postřeh. Bylo by rozhodně lepší provést výpočet výrazu dříve než v každém volání funkce. Výraz max + 1 je konstanta, kterou lze určit a zejména v tolika volání (100000) se to rozhodně vyplatí. Úryvek kódu by pak mohl vypadat např. takto:

// generovani histogramu
Random rand = new Random();
for (int i = 0, max++; i < throws; i++) vals[rand.Next(min, max)]++;

Ušetří se tím další čas.

Nahoru Odpovědět
19.3.2022 23:29
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:22.3.2022 8:38

Zkusim ti ten zelvickuz zapis prepsat trochu jinak. On vyuziva funkce z toho enumerate, kdete se podobaji sql prikazum a lambda zapisum z pythonu

Random rand = new Random();
(int Value, int Count)[] res =
Enumerable
.Range(0, pocet)
.Select(x => rand.Next(min, max))
.ToArray()
.GroupBy(x => x)
.Select(x => (Value: x.Key, Count: x.Count()))
.Where(x => x.Count > 1)
.OrderBy(x => x.Value)
.ToArray();
---
.Range(0, pocet)
.Select(x => rand.Next(min, max))
.ToArray() // aplikuj predchozi kroky na kazdy prvek v array

cyklus(i = 0 az pocet)
x = rand.Next(min, max)
array[i] = x
---

.GroupBy(x => x) // spojuj podle x
.Select(x => (Value: x.Key, Count: x.Count())) // pro kazde takto vnizkle x aplikuj funkci, ktera vytvori pole
// radek = array(value: radek.index, count: radek.count() )
.Where(x => x.Count > 1) // ponech radky, ktere maji count>1
.OrderBy(x => x.Value) // serad podle radek.value
.ToArray(); // zavolanim to array se vsechny predchozi kroky aplikuji na kazde X v array

To jsou proste nejake funkce toho enumerate, ktere se aplikuji jako call back a provedou se az v okamziku zavolani ToArray. Jestli to spravne chapu. V php bys to napsal takto, treba

.Range(0, pocet)
.ToArray()

<?php
$array = array();
foreach (range(0, pocet, 1) as $value) {
}
?>

---

.GroupBy(x => x)
.Select(x => (Value: x.Key, Count: x.Count()))
.ToArray();

<?php
$new_array = array();
foreach ($array as $row) {
    $group_key = $row; // shodou okolnosti je to i hodnota v tom poli $row=$value=$group_key (x => x)
    // ale dal ti to kvuli srozumitelnosti budu psat oddelene
    if (!isset($new_array[$group_key]))
        { $new_array[$group_key] = array( 'group-key' => $group_key, 'group-values' => array($row)); continue; }
    $new_array[$group_key]['group-values'][] = $row;
}
foreach ($new_array as &$value) { // ten and dela to, ze pracuje s handle, hodnotu vrati zpet do array
     $value = array('Value' => $value['group-key'], 'Count' => count($value['group-values']))
}
return $new_array;

/*
bez toho & by se to muselo naksat jako
foreach ($new_array as $key=>$value) {
     $new_array[$key] = array('Value' => $value['group-key'], 'Count' => count($value['group-values']))
}
*/
?>

Bohuzel C neovladam a nechce se mi premylset, jak by se to v nem zapisovat. Ale, snad je to ted jasnejsi.

 
Nahoru Odpovědět
22.3.2022 8:38
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:22.3.2022 8:43

Jako vstup dalsiho kroku se pouziva vystup z toho prechoziho. Vemes array1, predelas ji na array2 a pouzijes dal a udelas array3. Vetsina tech funkci prepise aktualni polozku v array pomoci funkce, kterou predas jako parametr. Group vytvari array novou. Vyhoda je, ze to mas krasne pojmenovane. Pouzivam neco podobne v javascriptu.

 
Nahoru Odpovědět
22.3.2022 8:43
Avatar
Fodo
Člen
Avatar
Odpovídá na DarkCoder
Fodo:28.3.2022 20:46

Odpisujem trochu neskôr, nebolo času.

Nedá sa to porovnať, som napísal preto, lebo ty si pri generovaní čísiel zrátavaš počty čísiel a priraďuješ do poľa v ktorom máš tieto čísla, ale v zadaní je že už máš postupnosť čísel, tzn. ja som to bral tak, že generovanie čísiel nie je zadaním úlohy, to je tam len vložené aby bolo s čím pracovať.

Ja som používal na meranie času DateTime.Now, uložil som do premennej pred a po, potom som odčítal. Nižšie výsledky z požitím Stopwatch(). V mojich predošlých výsledkoch bola pri „verzii 1pole“ chyba, preto bol čas cca 2 násobný.
Upravil som aj rozsah náhodných čísiel. V čase je zarátané aj generovanie náhodných čísel, pretože vo verzii 4 ho neviem oddeliť.

VERZIA 1:
00:00:00.0031505
00:00:00.1329323
00:00:00.1973771
00:00:00.2780999
00:00:00.3260673
00:00:00.3760173
00:00:00.4156473
00:00:00.4711198
00:00:00.5554469
00:00:00.6088071
00:00:00.6478704
00:00:00.7049988
00:00:00.7551432
00:00:00.8278246
00:00:00.9268072
Priemer = 0,45170685625
VERZIA 1pole:
00:00:00.0059360
00:00:00.1386639
00:00:00.2000115
00:00:00.2817709
00:00:00.3329280
00:00:00.3776999
00:00:00.4226900
00:00:00.4731310
00:00:00.5630960
00:00:00.6104298
00:00:00.6542619
00:00:00.7069290
00:00:00.7569540
00:00:00.8359176
00:00:00.9290137
Priemer = 0,455589575
VERZIA 1 (konštanta):
00:00:00.0083590
00:00:00.1410614
00:00:00.2031389
00:00:00.2888422
00:00:00.3357108
00:00:00.3793761
00:00:00.4253759
00:00:00.4749544
00:00:00.5651181
00:00:00.6121268
00:00:00.6563505
00:00:00.7089013
00:00:00.7586835
00:00:00.8396434
00:00:00.9311945
Priemer = 0,4580523
VERZIA 2:
00:00:00.0108665
00:00:00.1433131
00:00:00.2053980
00:00:00.2907392
00:00:00.3376136
00:00:00.3813673
00:00:00.4277097
00:00:00.4767404
00:00:00.5688399
00:00:00.6138255
00:00:00.6580721
00:00:00.7108595
00:00:00.7606082
00:00:00.8540468
00:00:00.9346598
Priemer = 0,460916225
VERZIA 4:
00:00:00.1034593
00:00:00.1702963
00:00:00.2314295
00:00:00.3079321
00:00:00.3541638
00:00:00.3975004
00:00:00.4475470
00:00:00.5131053
00:00:00.5929063
00:00:00.6301712
00:00:00.6790487
00:00:00.7372219
00:00:00.7869355
00:00:00.8904254
00:00:00.9637674
Priemer = 0,48786938125
VERZIA 5:
00:00:00.1267384
00:00:00.1915236
00:00:00.2746516
00:00:00.3242064
00:00:00.3704796
00:00:00.4135950
00:00:00.4696124
00:00:00.5506552
00:00:00.6071395
00:00:00.6459967
00:00:00.6969358
00:00:00.7534134
00:00:00.8162946
00:00:00.9179602
00:00:00.9910700
Priemer = 0,509392025
 
Nahoru Odpovědět
28.3.2022 20:46
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Fodo
DarkCoder:28.3.2022 21:43

Pošli sem pro kolik hodů a v jakém rozsahu mohou vygenerovaná čísla nabývat, ať můžeme porovnat verze. Pošlu sem verzi, jejíž vstupem bude pole o velikosti počtu hodů s vygenerovanými čísly ze kterých určím opakující se čísla která pak vypíši.

Nahoru Odpovědět
28.3.2022 21:43
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Fodo
DarkCoder:28.3.2022 21:56

K výsledkům:
To vážně se doba generování náhodných čísel, zjištění opakování a výpisu pohybuje kolem 500ms?

Nahoru Odpovědět
28.3.2022 21:56
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Fodo
DarkCoder:29.3.2022 2:00

Porovnej výsledky s následující verzí:

using System;
using System.Diagnostics;
public class Program {
    public static void Main() {
        int min = 1;
        int max = 20;
        int throws = 100000;
        int maxplus = max + 1;

        int[] nums = new int[throws];
        int[] vals = new int[maxplus];

        Stopwatch sw = new Stopwatch();
        sw.Start();

        // naplneni pole nahodne vygenerovanymi cisly
        Random rand = new Random();
        for (int i = 0; i < throws; i++) nums[i] = rand.Next(min, maxplus);

        // stanoveni poctu opakovani toho ktereho cisla
        for (int i = 0; i < throws; i++) vals[nums[i]]++;

        // vypis opakujicich se cisel a jejich pocet opakovani
        for (int i = min; i <= max; i++){
            if(vals[i] > 1) Console.WriteLine("Cislo {0} se opakuje {1}x", i, vals[i]);
        }

        sw.Stop();

        Console.WriteLine("sw = {0}", sw.Elapsed);
    }
}
Nahoru Odpovědět
29.3.2022 2:00
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
Fodo
Člen
Avatar
Odpovídá na DarkCoder
Fodo:29.3.2022 7:08

Dal som 1000 hodov. Rozsah 1- 20 (max = 21).

Spravil som pokus, hodil som tvoj kód do metódy HladajDuplicity6() a čas som meral pred a po volaní metódy (nie v nej) a vyšiel (1000 hodov, 15 opakovaní):

// priemer je v sekundách
VERZIA 1 - Priemer = 0,62299739375
VERZIA 1pole: - Priemer = 0,627510825
VERZIA 1 (konštanta): - Priemer = 0,632112025
VERZIA 2: - Priemer = 0,63575756875
VERZIA 4: - Priemer = 0,6608958125
VERZIA 5: - Priemer = 0,68036849375
VERZIA 6: - Priemer = 0,7052288375

//Tvoj kód, trochu upravený, ktorý som použil:
void HladajDuplicity6() // DarkCoder
{
        int[] nums = new int[pocet];
        int[] vals = new int[max];

        // naplneni pole nahodne vygenerovanymi cisly
        Random rand = new Random();
        for (int i = 0; i < pocet; i++) nums[i] = rand.Next(min, max);

        // stanoveni poctu opakovani toho ktereho cisla
        for (int i = 0; i < pocet; i++)
                vals[nums[i]]++;

        // vypis opakujicich se cisel a jejich pocet opakovani
        for (int i = min; i < max; i++)
                if (vals[i] > 1) Console.WriteLine("Cislo {0} se opakuje {1}x", i, vals[i]);
}

potom som spravil nový projekt a tvoj kód som dal priamo do metódy Main(), bez zmeny (100 000 hodov, 1 opakovanie)

sw = 00:00:00.0104522

Takže ten čas nerobí výkon toho kódu, ale pravdepodobne volanie metódy

Editováno 29.3.2022 7:10
 
Nahoru Odpovědět
29.3.2022 7:08
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Fodo
DarkCoder:29.3.2022 10:16

Pro 1000 hodů a rozsah <1,20> se celková doba rozhodně nebude pohybovat v řádech stovkách milisekund. Zabalení všech operací do funkce, která se volá 1x žádné časově navýšení nezpůsobí. Výpočet průměru máš chybný. Jednotlivé časy si budou blízké a nebudou stoupat s každým opakováním.

Nahoru Odpovědět
29.3.2022 10:16
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Fodo
DarkCoder:29.3.2022 12:19

Zde máš moji verzi do které byl přidán mechanismus opakování výpočtu pro eliminaci nepřesnosti. Nutno podotknout, že pro správnou funkci programu bylo třeba přidat nulování pole výsledků. Nicméně nulování pole o velikosti rozsahu generovaných čísel nezpůsobí bůhvíjaké navýšení celkového času. Počet opakování v programu byl stanoven na 10 (proměnná rep).

using System;
using System.Diagnostics;
public class Program {
    public static void Main() {
        int min = 1;
        int max = 20;
        int throws = 100000;
        int maxplus = max + 1;
        int rep = 10;

        int[] nums = new int[throws];
        int[] vals = new int[maxplus];

        Stopwatch sw = new Stopwatch();
        Random rand = new Random();

        sw.Start();
        for(int j = 0; j < rep; j++){
                for (int i = min; i <= max; i++) vals[i] = 0;
                for (int i = 0; i < throws; i++) nums[i] = rand.Next(min, maxplus);
                for (int i = 0; i < throws; i++) vals[nums[i]]++;
                for (int i = min; i <= max; i++){
                        if(vals[i] > 1) Console.WriteLine("Cislo {0} se opakuje {1}x", i, vals[i]);
                }
        }
        sw.Stop();
        Console.WriteLine("sw = {0}", sw.Elapsed / rep);
    }
}
Nahoru Odpovědět
29.3.2022 12:19
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:29.3.2022 13:11
// zajimalo by mne, zda je nejaky casovy rozdil mezi
for (int i = 0; i < pocet; i++) nums[i] = rand.Next(min, max);
for (int i = 0; i < pocet;) nums[i++] = rand.Next(min, max);
int i = 0; for (; i < pocet;) nums[i++] = rand.Next(min, max);
int i = 0; while (i < pocet) nums[i++] = rand.Next(min, max);

// a jestli by byl rozdil v tom, prepsat ten random do pomocne funkce
int i = 0; while (i < pocet) nums[i++] = random(); // return rand.Next(min, max);

Totiz, jako, kdyz se to prepise do pomocne funkce, procesor by na to mohl nahodit nejake optimalizace a vygenerovat si treba nekolik cisel dopredu.
A ten random ti trva 500 ms pro 1.000 nebo 10.000 cisel? Za stejny cas mam pole vygenerovane pres random a serazene pro n = 1.000.000 :) Viz, ten muj javascriptovy program. Ten cas by se ti mel pohybovat tak do 30 ms, myslim.
Na druhou stranu, jestli pouzivas c#, tak to mozne je. Minule jsem delal nejake pokusy v php, ktere je alternativou c# a byl dost problem pracovat s pixel mapou obrazku. Co jsem mel v js blik, tak to tam trvalo i 5s.

 
Nahoru Odpovědět
29.3.2022 13:11
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:29.3.2022 13:17

A zajimalo by mne, kdyby se misto toho sortu pouzil jiny algoritmus. Treba, takovy counting-sort je jasne nejrychlejsi, ale zere spousttu pameti.

 
Nahoru Odpovědět
29.3.2022 13:17
Avatar
Fodo
Člen
Avatar
Odpovídá na DarkCoder
Fodo:29.3.2022 14:15

Jasne, teraz ked to pozeram tak je u mne určite niečo zle, nekontroloval som si to všetko to robím trochu v zhone, lebo mám málo času, ale ak sa mi podarí (nájdem si nejaký čas) tak sa na to ešte pozriem.

 
Nahoru Odpovědět
29.3.2022 14:15
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Peter Mlich
DarkCoder:29.3.2022 14:26
#include <stdio.h>

#define pocet 10

int get10(void);

int main(void) {
        int nums[pocet];

        for (int i = 0; i < pocet; i++) nums[i] = 10;
        for (int i = 0; i < pocet;) nums[i++] = 10;
        int i = 0; for (; i < pocet;) nums[i++] = 10;
        int j = 0; while (j < pocet) nums[j++] = 10;
        int k = 0; while (k < pocet) nums[k++] = get10();

        return 0;
}

int get10(void) { return 10; }
; 10   :        for (int i = 0; i < pocet; i++) nums[i] = 10;

        mov     DWORD PTR i$4[rbp], 0
        jmp     SHORT $LN4@main
$LN2@main:
        mov     eax, DWORD PTR i$4[rbp]
        inc     eax
        mov     DWORD PTR i$4[rbp], eax
$LN4@main:
        cmp     DWORD PTR i$4[rbp], 10
        jge     SHORT $LN3@main
        movsxd  rax, DWORD PTR i$4[rbp]
        mov     DWORD PTR nums$[rbp+rax*4], 10
        jmp     SHORT $LN2@main
$LN3@main:

; 11   :        for (int i = 0; i < pocet;) nums[i++] = 10;

        mov     DWORD PTR i$5[rbp], 0
$LN5@main:
        cmp     DWORD PTR i$5[rbp], 10
        jge     SHORT $LN6@main
        movsxd  rax, DWORD PTR i$5[rbp]
        mov     DWORD PTR nums$[rbp+rax*4], 10
        mov     eax, DWORD PTR i$5[rbp]
        inc     eax
        mov     DWORD PTR i$5[rbp], eax
        jmp     SHORT $LN5@main
$LN6@main:

; 12   :        int i = 0; for (; i < pocet;) nums[i++] = 10;

        mov     DWORD PTR i$[rbp], 0
$LN8@main:
        cmp     DWORD PTR i$[rbp], 10
        jge     SHORT $LN9@main
        movsxd  rax, DWORD PTR i$[rbp]
        mov     DWORD PTR nums$[rbp+rax*4], 10
        mov     eax, DWORD PTR i$[rbp]
        inc     eax
        mov     DWORD PTR i$[rbp], eax
        jmp     SHORT $LN8@main
$LN9@main:

; 13   :        int j = 0; while (j < pocet) nums[j++] = 10;

        mov     DWORD PTR j$[rbp], 0
$LN11@main:
        cmp     DWORD PTR j$[rbp], 10
        jge     SHORT $LN12@main
        movsxd  rax, DWORD PTR j$[rbp]
        mov     DWORD PTR nums$[rbp+rax*4], 10
        mov     eax, DWORD PTR j$[rbp]
        inc     eax
        mov     DWORD PTR j$[rbp], eax
        jmp     SHORT $LN11@main
$LN12@main:

; 14   :        int k = 0; while (k < pocet) nums[k++] = get10();

        mov     DWORD PTR k$[rbp], 0
$LN13@main:
        cmp     DWORD PTR k$[rbp], 10
        jge     SHORT $LN14@main
        call    get10
        movsxd  rcx, DWORD PTR k$[rbp]
        mov     DWORD PTR nums$[rbp+rcx*4], eax
        mov     eax, DWORD PTR k$[rbp]
        inc     eax
        mov     DWORD PTR k$[rbp], eax
        jmp     SHORT $LN13@main
$LN14@main:

Můžeš porovnat 😀

Nahoru Odpovědět
29.3.2022 14:26
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Peter Mlich
DarkCoder:29.3.2022 15:02

Když už jsme u toho srovnávání:

#include <stdio.h>

int main(void) {
        int nums[10];

        int i = 0;
        nums[i] = 10;
        i++;

        int j = 0;
        nums[j++] = 10;

        return 0;
}
; 6    :        int i = 0;

        mov     DWORD PTR i$[rbp], 0

; 7    :        nums[i] = 10;

        movsxd  rax, DWORD PTR i$[rbp]
        mov     DWORD PTR nums$[rbp+rax*4], 10

; 8    :        i++;

        mov     eax, DWORD PTR i$[rbp]
        inc     eax
        mov     DWORD PTR i$[rbp], eax

; 9    :
; 10   :        int j = 0;

        mov     DWORD PTR j$[rbp], 0

; 11   :        nums[j++] = 10;

        movsxd  rax, DWORD PTR j$[rbp]
        mov     DWORD PTR nums$[rbp+rax*4], 10
        mov     eax, DWORD PTR j$[rbp]
        inc     eax
        mov     DWORD PTR j$[rbp], eax

Žádný rozdíl, první verze delší ale čitelnější.

Nahoru Odpovědět
29.3.2022 15:02
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
Fodo
Člen
Avatar
Odpovídá na DarkCoder
Fodo:29.3.2022 18:14

Problém bol že som nezresetoval stopky a tým pádom len pripočítaval čas, tu je výsledok po oprave:

VERZIA 1:
00:00:00.0078662
00:00:00.0077954
00:00:00.0015699
00:00:00.0061050
00:00:00.0040164
00:00:00.0041967
00:00:00.0042749
00:00:00.0094195
00:00:00.0048351
00:00:00.0059976
00:00:00.0128547
00:00:00.0426343
00:00:00.0085013
00:00:00.0049451
00:00:00.0085954
Priemer = 0,00835046875
VERZIA 1pole:
00:00:00.0151059
00:00:00.0027973
00:00:00.0017352
00:00:00.0111269
00:00:00.0157554
00:00:00.0023116
00:00:00.0029633
00:00:00.0023213
00:00:00.0072616
00:00:00.0030992
00:00:00.0087300
00:00:00.0160025
00:00:00.0041424
00:00:00.0204980
00:00:00.0037693
Priemer = 0,00735124375
VERZIA 1 (konštanta):
00:00:00.0149746
00:00:00.0023501
00:00:00.0018232
00:00:00.0044168
00:00:00.0066247
00:00:00.0019814
00:00:00.0026242
00:00:00.0083447
00:00:00.0043781
00:00:00.0026512
00:00:00.0030709
00:00:00.0056510
00:00:00.0135732
00:00:00.0165445
00:00:00.0029230
Priemer = 0,005745725
VERZIA 2:
00:00:00.0075594
00:00:00.0030023
00:00:00.0017847
00:00:00.0075826
00:00:00.0048605
00:00:00.0074082
00:00:00.0091671
00:00:00.0009517
00:00:00.0055804
00:00:00.0111440
00:00:00.0028649
00:00:00.0057456
00:00:00.0031519
00:00:00.0104646
00:00:00.0091387
Priemer = 0,0056504125
VERZIA 4:
00:00:00.1776225
00:00:00.0272220
00:00:00.0195320
00:00:00.0405773
00:00:00.0350710
00:00:00.0759398
00:00:00.0465790
00:00:00.0013034
00:00:00.0373368
00:00:00.0203371
00:00:00.1186381
00:00:00.0485791
00:00:00.0727754
00:00:00.1267088
00:00:00.0275542
Priemer = 0,05473603125
VERZIA 5:
00:00:00.0294297
00:00:00.0319403
00:00:00.0172996
00:00:00.0318518
00:00:00.0474263
00:00:00.0332643
00:00:00.0340469
00:00:00.0154010
00:00:00.0248230
00:00:00.0010695
00:00:00.0650704
00:00:00.0356247
00:00:00.0353870
00:00:00.0404237
00:00:00.0368345
Priemer = 0,02999329375
VERZIA 6:
00:00:00.0202313
00:00:00.0129938
00:00:00.0401931
00:00:00.0503583
00:00:00.0498097
00:00:00.0251319
00:00:00.0406313
00:00:00.0194508
00:00:00.0272151
00:00:00.0465620
00:00:00.0544188
00:00:00.0389841
00:00:00.0310209
00:00:00.0175428
00:00:00.0337978
Priemer = 0,03177135625
 
Nahoru Odpovědět
29.3.2022 18:14
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Fodo
DarkCoder:29.3.2022 19:02

Přijdou mi ty rozdíly mezi verzemi minimální a hodnoty přiliž velké pro 1000 hodů v rozsahu 1-20.

C# Online compiler

Jaká hodnota sw se vypíše ve výše uvedeném překladači pro tento program:

using System;
using System.Diagnostics;
public class Program {
    public static void Main() {
        int min = 1;
        int max = 20;
        int throws = 1000;
        int maxplus = max + 1;
        int rep = 50;

        int[] nums = new int[throws];
        int[] vals = new int[maxplus];

        Stopwatch sw = new Stopwatch();
        Random rand = new Random();

        sw.Start();
        for(int j = 0; j < rep; j++){
                for (int i = min; i <= max; i++) vals[i] = 0;
                for (int i = 0; i < throws; i++) nums[i] = rand.Next(min, maxplus);
                for (int i = 0; i < throws; i++) vals[nums[i]]++;
                for (int i = min; i <= max; i++){
                        if(vals[i] > 1) Console.WriteLine("Cislo {0} se opakuje {1}x", i, vals[i]);
                }
        }
        sw.Stop();
        Console.WriteLine("sw = {0}", sw.Elapsed / rep);
    }
}
Nahoru Odpovědět
29.3.2022 19:02
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
Fodo
Člen
Avatar
Odpovídá na DarkCoder
Fodo:29.3.2022 19:14

sw = 00:00:00.0002754

 
Nahoru Odpovědět
29.3.2022 19:14
Avatar
Fodo
Člen
Avatar
Fodo:29.3.2022 19:52

Potom som ešte spravil jeden pokus, prerobil som výpočet priemeru takto:

Console.WriteLine("sw = {0}", sw.Elapsed.TotalSeconds / rep);

pretože mi to v tom tvojom tvare nefungovalo cez Visual Studio. A spustil som v NTB a aj v online compileri a:

//NTB:
     sw = 0,020148322

//Online:
     sw = 0.000279046

Takže problém asi bude v NTB, ak aj toto vylúčiš, tak bude problém asi medzi stoličkou a NTB :)

Editováno 29.3.2022 19:54
 
Nahoru Odpovědět
29.3.2022 19:52
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Fodo
DarkCoder:29.3.2022 20:27

Vypadá to nejspíše na to že to způsobuje rozlišení časovače. Na laptopech, používá-li se pro měření časového intervalu funkce DateTime, může dojít k nepřesnosti 10ms+. Tato funkce se volá 2x, což může způsobit nepřesnost ve výši 20ms. Na těchto krátkých časových měření může dojít k právě tomuto chování. Všude preferují pro měření časových úseků používat Stopwatch před DateTime. Můžeš si chování vyzkoušet na změření samotného výpisu na obrazovku.

Co se týká výsledku měření času programu, ano, to je správná hodnota. Čas se pohybuje ve stovkách mikrosekund.

Editováno 29.3.2022 20:29
Nahoru Odpovědět
29.3.2022 20:27
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:30.3.2022 8:02

Cili, v zasade to vygeneruje stejny asm kod, jen jsou nektere instrukce v jinem poradi. Takze by to melo asi dosahovat stejne casy. Leda ma procesor nejake optimalizace pro konkretni posloupnosti instrukci.

Spis mne jako napadlo, ze kdyz se deklaruje promena predem, tak by cyklus mohl bezet rychleji.
Kdyz se udela i++ mimo deklaraci cyklu, tak se treba ztrati nejaky radek.
Jeste by teoreticky slo zkusit dat misto i++, ++i. To by se melo chovat rychleji. Ale, to ma asi spis vyznam uvnitr cyklu. V zahlavi to asi prekonvertuje stejne.

Samozrejme, kazda z tech uprav by mela byt v podstate bezvyznamna, max 0.01% nebo tak neco :) Spis jsem byl zvedavy. Ja treba stridam obcas pouziti while a for, nektere kody jsou prehlednejsi s while.

 
Nahoru Odpovědět
30.3.2022 8:02
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Peter Mlich
DarkCoder:30.3.2022 10:26

Ano, pokud nemá procesor nějaké své vnitřní optimalizace, tak budou dosažené časy stejné. Samozřejmě to také může být pouze hrubý nástřel a nakonec si nějakou tu optimalizaci najde. U některých zápisů ale skutečně může dojít k navýšení výkonu, respektive některý zápis bude rychlejší. Např. indexace a ukazatelová aritmetika.

Příklad ukazuje, jak 4 způsoby uložit hodnotu 10 do druhého prvku pole.

#include <stdio.h>

int main(void) {
        int nums[10], *p = nums;

        nums[1] = 10;
        *(nums + 1) = 10;
        p[1] = 10;
        *(p + 1) = 10;

        return 0;
}
; 4    :        int nums[10], *p = nums;

        lea     rax, QWORD PTR nums$[rbp]
        mov     QWORD PTR p$[rbp], rax

; 5    :
; 6    :        nums[1] = 10;

        mov     eax, 4
        imul    rax, rax, 1
        mov     DWORD PTR nums$[rbp+rax], 10

; 7    :        *(nums + 1) = 10;

        mov     DWORD PTR nums$[rbp+4], 10

; 8    :        p[1] = 10;

        mov     eax, 4
        imul    rax, rax, 1
        mov     rcx, QWORD PTR p$[rbp]
        mov     DWORD PTR [rcx+rax], 10

; 9    :        *(p + 1) = 10;

        mov     rax, QWORD PTR p$[rbp]
        mov     DWORD PTR [rax+4], 10

Výsledky jsou jistě zajímavé.

Nicméně je to jiný způsob zápisu než to, o čem byla řeč. Není na škodu a chybou rozepisovat kód do více řádků, nejen z důvodu čitelnosti, ale také z důvodu vedlejších efektů, které při zkrácených zápisech mohou nastat.

Ano, určitý rozdíl rychlosti v deklaraci proměnné je. Je rozdíl např. deklarovat pole staticky nebo až za běhu.

V programech se často používají cykly, ty mají své iterační proměnné, které se lokálně deklarují. Z hlediska rychlosti se nabízí možnost deklarovat proměnnou vně cyklu a používat ji pro více cyklů a vyhnout se tak nové deklaraci a zkrátit tak čas. To už jsou extrémní případy které mohou mít dopad na přehlednost programu, bezpečnost a rychlost psát kód. Ten drobný nárust rychlosti v tomto případě za to nestojí.

Co se týká inkrementace v prefixové podobě ++i proti postfixové podobě i++, pro vestavěné typy zde není žádný rozdíl. Drobný rozdíl je u tzv. compound typů, tedy typů co vytváří programátor, struktury, objekty, unie, tam je prefixová podoba zápisu nepatrně rychlejší.

Každý cyklus se pro něco hodí lépe pro něco méně. Důvodem je přehlednost a v určitých případech funkčnost, to zejména u rozdílu mezi do-while a while cykly. Ve skutečnosti jsou to jen varianty pro lidi, neboť překladače každý while a do-while cyklus zpracovávají do podoby for cyklu. Proto když jsem posílal asm výstup while cykly, nebyl tam žádný rozdíl. Já osobně preferuji for cyklus, neboť mohu dost zápisů napsat do jednoho řádku díky tomu, že inkrementační část je na stejném řádku na konci v těle cyklu.

Optimalizace je složitá, jsou místa kde to má smysl, třeba tvé porovnání všech možných algoritmů, tam se dá ušetřit spoustu času. A pak tyto drobné zápisy, které se hodí do extrémních situací a zabývat se jimi může zabrat příliš mnoho času. Ale je to bezesporu zajímavé. :-)

Nahoru Odpovědět
30.3.2022 10:26
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:30.3.2022 12:11

Takovy krasny priklad, kdy se mi vic libi while je treba opet v tom serazovani.
Pred cyklem se generuje nejake i, ktere je nebo neni vetsi nez i_end.
Zkopiruje to zustatek v poli arr1 do arr2

        while (i<i_end) arr2[k++] = arr1[i++];
// pres for je to takove divne :)
//      for (; i<i_end; i++, k++) arr2[k] = arr1[i];
//      for (; i<i_end; i++) arr2[k++] = arr1[i];
//      for (; i<i_end; ) arr2[k++] = arr1[i++];
Editováno 30.3.2022 12:12
 
Nahoru Odpovědět
30.3.2022 12:11
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Peter Mlich
DarkCoder:30.3.2022 12:46

A já bych zrovna dal přednost tomu prvnímu for, abych se vyhnul případným možným vedlejším efektům, které mohou nastat v přiřazovacích příkazech.. Navíc, krásná ukázka použití operátoru čárka.

Nahoru Odpovědět
30.3.2022 12:46
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Avatar
Peter Mlich
Člen
Avatar
Peter Mlich:30.3.2022 14:04

ja, treba, kdyz bych pouzil for, tak zasadne to druhe

for (; i<i_end; i++) arr2[k++] = arr1[i];
for (; i<i_end; i++) {arr2[k] = arr1[i]; k++; }

Oddelil bych cast, ktera tvori cyklus od toho ostatniho.

Editováno 30.3.2022 14:04
 
Nahoru Odpovědět
30.3.2022 14:04
Avatar
DarkCoder
Člen
Avatar
Odpovídá na Peter Mlich
DarkCoder:30.3.2022 14:19

Možností je mnoho. Je dobré udržovat věci pospolu, které mezi sebou souvisí.

for (; i<i_end; i++, k++) arr2[k] = arr1[i];
for (; i<i_end; ) arr2[k] = arr1[i], i++, k++;
for (; i<i_end; ) {
        arr2[k] = arr1[i];
        i++;
        k++;
}
for (; i<i_end; ) {
        arr2[k] = arr1[i];
        i++, k++;
}

Nechť si každý vybere co mu vyhovuje.. :-)

Nahoru Odpovědět
30.3.2022 14:19
"I ta nejlepší poučka postrádá na významu, není-li patřičně předána." - DarkCoder
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 50 zpráv z 50.