praegune kellaaeg 20.04.2024 01:35:20
|
Hinnavaatlus
:: Foorum
:: Uudised
:: Ärifoorumid
:: HV F1 ennustusvõistlus
:: Pangalink
:: Telekavad
:: HV toote otsing
|
|
autor |
sõnum |
|
Rauno266
Kreisi kasutaja
liitunud: 03.07.2009
|
24.01.2015 11:58:50
C#: Listi loopimine üle iseenda kõige efektiivsemal viisil |
|
|
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 |
|
|
Timukas0
HV kasutaja
liitunud: 20.03.2007
|
24.01.2015 13:46:48
|
|
|
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 |
|
|
blinx
HV vaatleja
liitunud: 28.11.2009
|
25.01.2015 03:31:12
|
|
|
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 |
|
|
HellWalKeR
HV kasutaja
liitunud: 20.11.2006
|
25.01.2015 13:54:11
|
|
|
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 |
|
|
sigakoer
Kreisi kasutaja
liitunud: 23.01.2004
|
25.01.2015 15:28:41
|
|
|
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 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 |
|
|
wrx123
HV kasutaja
liitunud: 21.03.2006
|
25.01.2015 15:37:24
|
|
|
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 |
|
|
andresv
HV kasutaja
liitunud: 06.12.2004
|
|
Kommentaarid: 5 loe/lisa |
Kasutajad arvavad: |
|
:: |
0 :: |
0 :: |
5 |
|
tagasi üles |
|
|
|
lisa lemmikuks |
|
|
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.
|