Avaleht
uus teema   vasta Tarkvara »  Programmeerimine »  C#: Listi loopimine üle iseenda kõige efektiivsemal viisil märgi kõik teemad loetuks
märgi mitteloetuks
vaata eelmist teemat :: vaata järgmist teemat
Hinnavaatlus :: Foorum :: Uudised :: Ärifoorumid :: HV F1 ennustusvõistlus :: Pangalink :: Telekavad :: HV toote otsing
autor
sõnum Saada viide sõbrale. Teata moderaatorile
otsing:  
Rauno266
Kreisi kasutaja

liitunud: 03.07.2009




sõnum 24.01.2015 11:58:50 C#: Listi loopimine üle iseenda kõige efektiivsemal viisil vasta tsitaadiga

Tere

On vaja töödelda ühte faili, milles on sees ostuandmed. Failist loen kõigepealt välja, mitu erinevat toodet mingil ajaperioodil müüdi (Erinevad tooted on listis).
Näiteks: Klient 1 ostis Raamatu 1 ja Raamatu 3, Klient 2 ostis Raamatu 1 ja Raamatu 4. Siis listi sisu oleks Raamat 1, Raamat 3 ja Raamat 4.

Edasi tahan koostada risttabeli toodetest, et arvutada välja kui tihti osteti mingeid tooteid omavahel koos.
Näiteks:
Raamat 1, Raamat 3, 25 korda
Raamat 1, Raamat 4, 15 korda
Raamat 3, Raamat 4, 2 korda

Selline asi on igas olukorras lihtne, kui müüdigi ainult kolme erinevat toodet. Minu ostuandmete failis on aga 900000 rida ning ajaperioodi jooksul on müüdud 16470 erinevat raamatut. Nüüd kui moodustada lihtne risttabel raamatutest, siis saan ~270mln erinevat paari. Kuna järjekord paaris (Raamat 1, Raamat 3 = Raamat 3, Raamat 1) ei ole oluline, siis saan selle listi suuruse jagada kahega. Lisaks ei moodustu paare samast tootest (Raamat 1, Raamat 1), ning seetõttu saab listist eemaldada veel 16470 kirjet.

Nüüd, kui enamvähem sain kirjeldatud probleemi, küsiksingi, kuidas seda risttabelit koostada oleks kõige efektiivsem. Minu ponnistused päädisid sellega, et kasutasin nested loopi, kus ma loopin mõlemas loopis üle erinevate raamatute listi (16470 kirjet)

foreach(int i = 0; i < goods.Count; i++){
foreach(int j = 1; j<goods.Count; j++){
if( j > i){
CalculatePurchases(goods[i],goods[j]);
}
}
}

Selle implementatsiooniga käis kõiki kirjeid läbi umbes 50min, mis on natuke aeglane minu jaoks. Arvutil on i5-4690k prose ning 16GB RAM-i. Viga ei tohiks olla arvutis. Algoritm on tehtud kiirelt esimese selgushetke peale ja ei oska hetkel ise oma koodis nõrkasid kohtasid leida. Sisuliselt siis mida ma teha tahan on leida kõik kahest tootest koosnevad kombinatisoonid. Kas keegi on midagi sellist varem teinud? Või oskab soovitada mingeid classe, mida kasutada selliste tegevuste jaoks (HashSet ei aidanud).
Kommentaarid: 41 loe/lisa Kasutajad arvavad:  :: 2 :: 0 :: 39
tagasi üles
vaata kasutaja infot saada privaatsõnum
Timukas0
HV kasutaja
Timukas0

liitunud: 20.03.2007




sõnum 24.01.2015 13:46:48 vasta tsitaadiga

Ei tea täpselt CalculatePurchases sisu, aga eeldatavasti saaksid sama asja teha juba faili läbi lugedes.
Nt on looad array purchases ja hakkad faili lugema:
1. rida "Klient 1 ostis Raamatu 1 ja Raamatu 3": puchases[1][3]++;
2. rida "Klient 2 ostis Raamatu 1, Raamatu 3 ja Raamatu 4": puchases[1][3]++; purchased[1][4]++; puchases[3][4]++;
...

Ei tea C# eripärasid. Kui saad, loo see array dünaamiliselt; kui mitte, siis käi fail ühe korra läbi, loo tühi array (16470*16470) ja siis teise loopiga lisa arvud.
Kommentaarid: 3 loe/lisa Kasutajad arvavad:  :: 0 :: 0 :: 3
tagasi üles
vaata kasutaja infot saada privaatsõnum
blinx
HV vaatleja

liitunud: 28.11.2009




sõnum 25.01.2015 03:31:12 vasta tsitaadiga

Ma ei tea, kas saan õigesti aru, aga kui on failis Klient 1 ostis Raamatu 1 ja Raamatu 3, Klient 2 ostis Raamatu 1 ja Raamatu 4 jne., ja vaja on ainult arvutada välja kui tihti osteti mingeid tooteid omavahel koos, siis tee Dictionary<string,uint> dict;
1. Vaata dict.containskey("raamat1raamat2"), kui jah siis value++; (containskey on O(1) operation)
Tõenäoliselt on paremaid lahendusi veel.

_________________
'Just buy everything then you're safe'
tagasi üles
vaata kasutaja infot saada privaatsõnum
HellWalKeR
HV kasutaja
HellWalKeR

liitunud: 20.11.2006




sõnum 25.01.2015 13:54:11 vasta tsitaadiga


foreach(int i = 0; i < goods.Count; i++){
    foreach(int j = 1; j<goods.Count; j++){
        if( j > i){
            CalculatePurchases(goods[i],goods[j]);
        }
     }
}

Esimene asi on see, et kaota ära IF lause ja muuda kood selliseks

foreach(int i = 0; i < goods.Count; i++){
    foreach(int j = i + 1; j<goods.Count; j++){
        CalculatePurchases(goods[i],goods[j]);
   }
}


See peaks aega kõvasti alla tõmbama.
Kommentaarid: 5 loe/lisa Kasutajad arvavad:  :: 1 :: 0 :: 3
tagasi üles
vaata kasutaja infot saada privaatsõnum
sigakoer
Kreisi kasutaja

liitunud: 23.01.2004




sõnum 25.01.2015 15:28:41 vasta tsitaadiga

Eeldusel et tavaliselt ühekorraga ostetakse ainult mõni raamat, mitte tuhandeid erinevaid korraga, siis oleks mõttekam arvet pidada perioodi vältel ostetud raamatukombinatsioonide endi üle -- mis tegelikult ongi ju see mida vaja.

Ehk siis võtta aluseks objekt nagu kombinatsioon, a la Combo(raamatA, raamatB), kus raamatA ja raamatB on alati ühes, ISBN või nime vms alusel sorteeritud järjekorras.

Näiteks kui inimene ostab 4 raamatut 1-4, siis oleks löödaks see tellimus lahti seal esinenud raamatukombinatsioonide massiiviks
Combo[] combosInOrder = findBookCombos(order);

kus findBookCombos() tagastaks mingi säärase array

[
    Combo(raamat1, raamat2),
    Combo(raamat1, raamat3),
    Combo(raamat1, raamat4),
    Combo(raamat2, raamat3),
    Combo(raamat2, raamat4),
    Combo(raamat3, raamat4)
];

Ma C# progeja ei ole ning süntaks on lambist java/js segakeel , aga loodetavasti saate aru icon_razz1.gif Sisuliselt HellWalKeR andiski tolle findBookCombos sisu:

function findBookCombos(order) {
   Combo[] combos = new Combo[];
   goods = sort(order.goods); // mingi raamatute eelnev sortimine on vajalik
   foreach(int i = 0; i < goods.Count; i++){
       foreach(int j = i + 1; j<goods.Count; j++){
           combos.push(new Combo(goods[i],goods[j]));
      }
   }
   return combos;
}



Ülal peaksid sa siis ühteainsat dictionary, kus keydeks on kõik seninähtud Combo objektid ja value'deks nende esinemise hulk.


{
    Combo(raamat1, raamat2) => 1,
    Combo(raamat1, raamat3) => 14
    Combo(raamat2, raamat3) => 3
    ... jne
}


ning käies tellimuste listi vaid üks kord üle, koostada iga tellimuse kohta combode massiiv ning lisada-suurendada seal dictionarys igale selles sisalduvale combole vastavat väärtust.


Order[] orders = blablabla();
Dictionary<Combo> dict = new Dictionary<Combo>();
for (order in orders) {
   Combo[] combosInOrder = findBookCombos(order);
   for (combo in combosInOrder) {
       dict.put(combo, dict.get(combo)+1);
   }
}


Lõpuks oleks sul seal dict'is siis kõik kombinatsioonid ja nende esinemiste arvud O(tellimustearv * (keskmine tellimuse raamatute arv^2)) iteratsiooniga ning ilma megaristtabelit vajamata.


viimati muutis sigakoer 25.01.2015 15:40:33, muudetud 1 kord
Kommentaarid: 33 loe/lisa Kasutajad arvavad:  :: 0 :: 1 :: 32
tagasi üles
vaata kasutaja infot saada privaatsõnum
wrx123
HV kasutaja

liitunud: 21.03.2006



Autoriseeritud ID-kaardiga

sõnum 25.01.2015 15:37:24 vasta tsitaadiga

blinx kirjutas:
Ma ei tea, kas saan õigesti aru, aga kui on failis Klient 1 ostis Raamatu 1 ja Raamatu 3, Klient 2 ostis Raamatu 1 ja Raamatu 4 jne., ja vaja on ainult arvutada välja kui tihti osteti mingeid tooteid omavahel koos, siis tee Dictionary<string,uint> dict;
1. Vaata dict.containskey("raamat1raamat2"), kui jah siis value++; (containskey on O(1) operation)
Tõenäoliselt on paremaid lahendusi veel.

"raamat1raamat2" oleks parem teha BookPair vms klassiks ja overrideda GetHashCode meetod nii, et sõltumata raamatute järjekorrast tuleks sama hashcode väärtus:

class BookPair
{
    public string FirstBook { get; set; }
    public string SecondBook { get; set; }

    public override int GetHashCode() { .... }
}


https://msdn.microsoft.com/en-us/library/system.object.gethashcode%28v=vs.110%29.aspx
Kommentaarid: 16 loe/lisa Kasutajad arvavad:  :: 0 :: 0 :: 15
tagasi üles
vaata kasutaja infot saada privaatsõnum
andresv
HV kasutaja

liitunud: 06.12.2004




sõnum 26.01.2015 12:37:30 vasta tsitaadiga

Kuna sul eeldatavasti vaja ainult ülemist otsa nendest koos ostetud raamatutest, siis uuri count-min algoritmi ning mälu jääb ülegi. icon_smile.gif
http://en.wikipedia.org/wiki/Count%E2%80%93min_sketch
Kommentaarid: 5 loe/lisa Kasutajad arvavad:  :: 0 :: 0 :: 5
tagasi üles
vaata kasutaja infot saada privaatsõnum
näita postitusi alates eelmisest:   
uus teema   vasta Tarkvara »  Programmeerimine »  C#: Listi loopimine üle iseenda kõige efektiivsemal viisil
[vaata eelmist teemat] [vaata järgmist teemat]
 lisa lemmikuks
näita foorumit:  
 ignoreeri teemat 
sa ei või postitada uusi teemasid siia foorumisse
sa ei või vastata selle foorumi teemadele
sa ei või muuta oma postitusi selles foorumis
sa ei või kustutada oma postitusi selles foorumis
sa ei või vastata küsitlustele selles foorumis
sa ei saa lisada manuseid selles foorumis
sa võid manuseid alla laadida selles foorumis



Hinnavaatlus ei vastuta foorumis tehtud postituste eest.