kompleksowe sumowanie tablic w stl, albo bez

0

Mamy dane w postaci rekordów np. typu:

int grupa, id;
double ilość, cena, wartość;

i są tego spore ilości - tysiące, znaczy tabelek rekordzików tego typu,
a ja chcę to scalić - posumować, i do tego różnie:

  1. chcę sumę w poszczególnych grupach, czyli tak:
    < grupa, id> = klucz, i suma ilości, suma wartości

  2. sumujemy po samym id - sumy zbiorcze:
    <id> = klucz, sumy...; grupa = cokolwiek - nieistotne;

  3. ale niekiedy muszę to sumować w ramach kilku grup, np. tak:
    (grupa = 1,4,8 lub 99; id>, sumy...

  4. albo i nawet tak:
    grupa = dowolna i id = dowolny, suma - jedna!

1

Czyli, są struktury:

struct {
int gupa, id;
double ilosc, cena, wartosc;
};

I co teraz chcemy, wyłuskac te, z daną grupą i zsumować ich pola wartość? To chyba nie powinno być trudne, wyszukiwanie liniowe i jednoczesne sumowanie.

0

W sumie struktura zawiera więcej:

np. data + ta struktura,

mnie te daty nie interesują: chcę to posumować tylko.

Przykład danych wejściowych - do sumowania:

data, grupa, id, ilość, cena

t1 =
555, 2, 166, 55.2 kg, 2zł [ wartość = 55.2 x 2 = ... tego nie ma - to obliczamy sobie]
555, 2, 555, 100 szt, 1.1
555, 2, 557, ...
....

t2 =
557, 2, 555, 10 szt, 1.2 zł
557, 3, 111, ...

t3 =
557, 1, 555, 30 szt, 1.2 zł
557, 5, 111, ...

itd. - są tysiące takich tabelek!

należy to najpierw posumować wg <grup, id> czyli po wszystkich datach.

1
map<pair<group_t,id_t>,pair<count_t,amount_t> Summary;
pair<count_t,amount_t> &rec=Summary[make_pair(group,id)];
rec.first+=add_count;
rec.second+=add_value;
0

ma być suma tablic, czyli operacja typu:

t_sum = t1 + t2 + t3 + ...

czyli wynikiem jest też tablica, w której mamy... unikalne sumy po <g,id>.

i dalej - wyciągamy:
t_sum[g=dowolne, po id] = rec(il, wart) -> co np. wywalam na ekran, albo używam do dalszych obliczeń.

1

Starsznie niejasno Piszesz; co jest suma tablic, przecież w takich strukturach nie ma zdefiniowanego dodawania.

0

od tego jest obiektowość aby można było sumować - cokolwiek.

np. tak:
TTree a();
TList b(one);

a += b;
b.load(two);

a+=b;

itd.

proste?

b.load(8)
c.load(87);

a = b + c;

1

Nie wiem co ma z tym wspólnego obiektowość; nie wytłumaczyłeś, jak jest definicja dodawania dla Twoich tablic.

0

Było wytłumaczone na czym polega takie dodawanie - scalanie list rekodów.

Mamy tę listę z sumami, i teraz chcemy dodać nowy element, czyli typu: r = [g,id, il, cena]

zatem musimy tak czy siak przeszukać całą tę listę, czy drzewko, i znaleźć:
ref = find(r); // szuka key = <g,id>

i teraz:
if( !ref ) insert(r); // nie ma takiego: wsadzamy nowy element z kluczem key = <g,id> i + dane: il, il x cena;
else ref += r; // znaleziono, więc dokładamy tylko do obecnego: il += r.il; w += r.il x r.cena;

........

ale potem, przed wylistowaniem tych sum, np. na ekran, trzeba to jeszcze posortować, ale nie wg: id,
lecz po nazwach dowiązanych, tz. mamy tu faktycznie rekordy typu:
[id, nazwa]

3

Skoro wszystko wiesz co i jak zrobić to po co ten wątek? Po prostu zaimplementuj to.
Może w końcu zobaczymy jakiś real code naczelnego krytyka wszystkich i wszystkiego :>

0
#include <iostream>
#include <tuple>
#include <map>
using namespace std;

typedef size_t id_t,group_t;
typedef unsigned count_t;
typedef double amount_t;
typedef string name_t;
typedef tuple<id_t,name_t,group_t> key_t;
typedef pair<count_t,amount_t> value_t;
typedef map<key_t,value_t> map_t;

void add(map_t &map,const key_t &key,const value_t &value)
{
	value_t &dst=map[key];
	dst.first+=value.first;
	dst.second+=value.second;
}


int main()
{
	map_t map;
	add(map,key_t(12,"two",2),value_t(10,13.5));
	add(map,key_t(12,"four",4),value_t(10,13.5));
	add(map,key_t(11,"two",2),value_t(10,13.5));
	add(map,key_t(10,"one",1),value_t(10,13.5));
	add(map,key_t(12,"two",2),value_t(10,13.5));
	add(map,key_t(10,"two",2),value_t(10,13.5));
	add(map,key_t(11,"one",1),value_t(10,13.5));
	add(map,key_t(12,"two",2),value_t(10,13.5));
	add(map,key_t(11,"four",4),value_t(10,13.5));
	for(const auto &pair:map)
	{
		cout<<get<0>(pair.first)<<","<<get<1>(pair.first)<<" => "<<pair.second.first<<" / "<<pair.second.second<<endl;
	}
	return 0;
}

lub

#include <iostream>
#include <tuple>
#include <map>
using namespace std;

typedef size_t id_t,group_t;
typedef unsigned count_t;
typedef double amount_t;
typedef string name_t;
typedef tuple<id_t,name_t,group_t> key_t;
typedef pair<count_t,amount_t> value_t;
typedef map<key_t,value_t> map_t;

value_t &operator+=(value_t &value,const value_t &add)
{
	value.first+=add.first;
	value.second+=add.second;
	return value;
}

int main()
{
	map_t map;
	map[key_t(12,"two",2)]+=value_t(10,13.5);
	map[key_t(12,"four",4)]+=value_t(10,13.5);
	map[key_t(11,"two",2)]+=value_t(10,13.5);
	map[key_t(10,"one",1)]+=value_t(10,13.5);
	map[key_t(12,"two",2)]+=value_t(10,13.5);
	map[key_t(10,"two",2)]+=value_t(10,13.5);
	map[key_t(11,"one",1)]+=value_t(10,13.5);
	map[key_t(12,"two",2)]+=value_t(10,13.5);
	map[key_t(11,"four",4)]+=value_t(10,13.5);
	for(const auto &pair:map)
	{
		cout<<get<0>(pair.first)<<","<<get<2>(pair.first)<<" => "<<pair.second.first<<" / "<<pair.second.second<<endl;
	}
	return 0;
}
0

Chyba zbytnio się rozpędziłeś z tym wsadzaniem nazw do map;

te nazwy są dość długie, do 200 znaków powiedzmy, i jest ich kilka tysięcy, więc nie ma sensu tego wpisywać do map...
bo samych elemetów <g, id> są tu grube tysiące, powiedzmy że 100 tysięcy!

Dlatego mój pomysł: sumujemy po id, a dopiero wyniki sortujemy - po nazwach,
i praktycznie bez używania tych nazw!, bo tu wystarczy posortować

te rekordy [id, nazwa] po nazwach (co już jest zrobione!),
i potem wystarczy to... 'transponować' - czyli zrobić 'inwersję' - złożoność o(1).

....

poza tym chyba źle zrozumiałeś co tu reprezentują te dane:

map[key_t(12,"two",2)]+=value_t(10,13.5);
map[key_t(12,"four",4)]+=value_t(10,13.5);

takie coś jest niemożliwe, ponieważ pary typu: <id, nazwa>
są te id są tylko w celu optymalizacji - dla oszczędzenia miejsca:

nazwy są kluczem właściwym, a te id są pomocniczy tylko,
czyli: nazwa -> id; jedoznacznie!

przykład - nieprawidłowego kodowania:
12, krowa
12, koza

takie coś jest niemożliwe, bo krowa != koza.

To id jest po prostu synonimem - skrótem nazwy:
koza = 1
byk = 2
rak = 3
baran = 4
piwo = 5
kura = 6

co po posortowaniu (po nazwach!) otrzymamy pary <id, nazwa>:
4, baran
2, byk
1, koza
...

0

Postaw nazwę w tuple jako pierwszą: typedef tuple<name_t,id_t,group_t> key_t;
*Oczywiście przy wywołaniu też trzeba zmienić kolejność.

#include <iostream>
#include <tuple>
#include <map>
using namespace std;

typedef size_t id_t,group_t;
typedef unsigned count_t;
typedef double amount_t;
typedef string name_t;
typedef tuple<name_t,group_t> key_t;
typedef pair<count_t,amount_t> value_t;
typedef map<key_t,value_t> map_t;

value_t &operator+=(value_t &value,const value_t &add)
{
    value.first+=add.first;
    value.second+=add.second;
    return value;
}

int main()
{
    map_t map;
    map[key_t("two",2)]+=value_t(10,13.5);
    map[key_t("four",4)]+=value_t(10,13.5);
    map[key_t("two",2)]+=value_t(10,13.5);
    map[key_t("one",1)]+=value_t(10,13.5);
    map[key_t("two",2)]+=value_t(10,13.5);
    map[key_t("two",2)]+=value_t(10,13.5);
    map[key_t("one",1)]+=value_t(10,13.5);
    map[key_t("two",2)]+=value_t(10,13.5);
    map[key_t("four",4)]+=value_t(10,13.5);
    for(const auto &pair:map)
    {
        cout<<get<0>(pair.first)<<","<<get<2>(pair.first)<<" => "<<pair.second.first<<" / "<<pair.second.second<<endl;
    }
    return 0;
}
0

skecz polega na tym, że nie masz bezpośredniego dostępu do nazw.

rekordy są w postaci: [data, ..., grupa, id, il, cena]

gdybym chciał tu ładować nazwy do każdego, czyli coś w stylu: getName(id),
całe to sumowanie trwałoby z 1000 razy dłużej, czyli kilka godzin zapewne, co jest nie do zaakceptowania!

............

dlatego takie coś załatwiamy inaczej:

mamy indeks typu:
[id1, id2, id3, ...] kolejność wg nazw.

natomiast sumujemy po id, czyli otrzymamy sekwencję:
[1,2,3,4, ...]

zatem aby uzyskać sort wg nazw, wystarczy to zwyczajnie 'inwertować' ,
czyli wykonać operację analogiczną do odwracania macierzy.

t_nazwy[t_numery[i]] = ?

2

Obłędnie ględzisz. Podaj przykładowe dane wyjściowe i dla tych danych podaj jakie sumy chcesz uzyskać, policz w excelu i najlepej udostępnij ten excel aby widziałem co z czym sumujesz.

0

Przecież to jest typowa sprawa bazy z zakupami albo z redystrybucji.

Np. paragon - taki ze sklepu:

data zakupu: 11-11-11, 17:57':37'', kasa nr 7, market numer 66, itd. +
lista zakupów:
[grupa, towar, ilość, cena]

  • 55, marchewka, 5kg, 2zł
  • 55, śliwki, 3kg, 5zł
  • 67, buty marki gumowiec, 3 szt, 100zł
  • 12, pasta do nosa, 6 szt, 20 zł
    ...

zamiast nazw towarów używamy numerów oczywiście: id, bo szkoda marnować miejsce.
55 - to grupa - tu np. warzywa
67 - buty,... 1 - protezy, 2 - proszki, 3 - łańcuchy; 99 - kluchy

I takich paragonów masz np. 100000 w bazie, co należy scalić, posumować...

...............
Wariant II - z redystrybucją zakupów.

mamy tamten paragon - zakup, ale rozdzielamy dodatkowo, np.:
55, marchewka, 5kg, 2zł

  • alicja: 2 kg
  • baśka: 3 kg

55, śliwki, 3kg, 5zł

  • maciuś: 3.5 kg
  • wacuś: 0.5 kg
  • baśka: 1 kg

67, buty marki gumowiec, 3 szt, 100zł

  • kaśka: 1 szt
  • baśka: 1 szt
  • kryśka: 1 szt

12, pasta do nosa, 6 szt, 20 zł

  • felek: 1
  • pantofelek: 1
  • zośka 3
    ...

OK.

I teraz chcemy z tych dostaw - paragonów coś takiego uzyskać:

  1. ile i co zakupiono w sumie z marketu nr 55 (z sumami po grupach, albo i szczegółowo - po towarach)
  2. ile, co i kto kupił z marketu nr 55, czyli:
  • co kupił maciuś: lista towarów z... podziałem na grupy, oczywiście
  • co kaśka
  • itd.
    ...

i co - wsio jasne teraz? :)

1

Takie rzeczy robisz prostymi zapytaniami w SQL-u

0

A co jeżeli:
55, marchewka, 5kg, 2zł
55, śliwki, 3kg, 5zł
55, ananas, 1szt, 7zł
Jaki ma być wynik sumowania: 8(kg+szt) 32zł?

Skąd się wzięli (alicja,baśka,maciuś,wacuś,kaśka,kryśka) z paragonów:

55, marchewka, 5kg, 2zł
55, śliwki, 3kg, 5zł
67, buty marki gumowiec, 3 szt, 100zł
12, pasta do nosa, 6 szt, 20 zł

?

Na każde takie zapytanie będziesz musiał przygotować klasę lub wystarczy odpowiednie zapytania SQL o ile masz to w przyzwoitej bazie danych.

0

Ilości sumujemy dla tych samych id, czyli po towarach.

Wartości możemy sumować dowolnie, w tym i zwłaszcza: grupami, baśkami, sklepami.

baśka, kaśka, itd. to dodatkowy motyw: z opcją dalszej redystrybucji zakupów.

SQL raczej za cienki jest do mojej aplikacji.
Od biedy mógłbym nawet użyć SQL, ale raczej w roli klasycznego dostawcy danych... a nie do pełnego zastąpienia kodu c++ - złożonych obliczeń.

0

Gdzie przy paragonach baśka, kaśka, ... itd?

Podaj strukturę tablic mądralo.
screenshot-20200808154120.png
Na ekranie:
grupy: wg czego grupujemy;
klucze: wg których pół zbieramy klucze.
Po wywołaniu raportu generowany w locie SQL, odpalany i pokazywany w drzewku, sumowanie dla każdej gałęzi.
Niestety wszystko pod VCL którego nie lubisz (podejrzewam z racji ignorancji) :D

0

Finalna struktura jest tu drzewem, w stylu:

  1. Sklep w Nowej Hucie nr 45 [ten root wybieramy z listy sklepów]
    1.1 Osiedle na ul. Bonifacego
    1.1.1 Blok nr 1
    • kaśka
    • baśka
    • maciuś
    • ...

1.1.2 Blok nr 2

  • olek
  • bolek
  • zośka

itd.

Potem klikam - zaznaczam pozycję:

  • olek - i to ma wydrukować listę towarów, zakupów, z podziałem na grupy.
  • Blok 2 - wywala mi sumy z tej gałęzi, czyli olek + bolek, ...
  • Osiedle na ul. Bonifacego - wywala mi zakupy z całego osiedla

To tylko mały fragmencik całości - mamy tu jeszcze: koszty dostaw, i wiele innych fajnych spraw... mam opowiadać dalej, czy już wysiadacie? :)

1

Jak paragon jest powiązany z tym drzewkiem?

0

VCL to ja używałem chyba z 20 lat temu... błazenada w porównaniu z OWL - poprzednikiem tego badziewia, i nawet z MFC - prowizorycznym klonem OWL.
Pewnie dlatego Borland wykitował, bo wszedł w to vcl, zamiast rozwijać OWL po prostu.

Co do paragonów, czy dowolnych rachunków, sprawa tu wygląda z grubsza tak:

  1. kupujemy towary - surowce, materiały, energię, itd. albo i nawet bierzemy swoje - z magazynu
  2. potem to zużywamy, czyli redystrybucja - i tam jest 'konsumpcja', w konkretnych miejscach

i stąd oczywisty wniosek:
3. każdy paragon musimy dodatkowo rozpisać - po tych miejscach, konsumentach.

Finalnie otrzymujemy dane w postaci drzewa... miejsc alokacji kosztów: konsumpcji.

...........
I nie dotyczy to tylko handlu, lecz ma powszechne zastosowanie, np. gdy budujemy dom, albo most, czy kiosk,
wtedy mamy inwestycję, powiedzmy, którą sobie rozkładamy - w czasie i po miejscach.

  1. przygotowanie gruntu
  • niwelacja
  • utwardzanie
  • odwadnianie
  1. robimy fundamenty
  • kopanie
  • lanie żwiru i betonu
  • ...
  1. ściany
    a. przygotowanie
  • rusztowania, dźwigi, itp.

b. stawianie ścian - i tu wchodzą milony cegieł, tony zaprawy...

  • murowanie
  • tynkowanie: gipsy, itp.
  • izolacje: folie, szmaty, itd.
  • ...
  1. dach
  2. instalacje
  3. dekoracje
  4. koronacja
1

Dowolna ilość kluczy, dowolna ilość grup.
Jedyne trzeba uważać aby nie sumować grochu z kapustą.

#include <iostream>
#include <iomanip>
#include <vector>
#include <string>
#include <algorithm>
#include <map>
using namespace std;

struct cash
{
	double count,price,amount;
	cash(double count=0,double price=0):count(count),price(price),amount(count*price) {}
	cash &operator+=(const cash &add)
	{
		count+=add.count;
		price+=add.price;
		amount+=add.amount;
		return *this;
	}
};
typedef vector<size_t> group;
typedef vector<string> params;
struct data
{
	cash cs;
	params pr;
};
typedef vector<data> datasource;
struct key
{
	size_t parent;
	string value;
	key(size_t parent=0,const string &value=string()):parent(parent),value(value) {}
	bool operator<(const key &k)const
	{
		if(parent<k.parent) return true;
		if(parent>k.parent) return false;
		return value<k.value;
	}
};
struct idxcash
{
	size_t idx;
	cash sumary;
	idxcash(size_t idx=0,const cash &sumary=cash()):idx(idx),sumary(sumary) {}
};
typedef map<key,idxcash> tree;

void maketree(tree &tr,const datasource &data,const group &grp)
{
	tr.clear();
	size_t idx=0;
	key kk(0,"razem");
	idxcash &top=tr[kk]=idxcash(++idx);
	for(auto rec:data)
	{
		top.sumary+=rec.cs;
		size_t parent=1;
		for(size_t level=0;level<grp.size();++level)
		{
			idxcash &ics=tr[key(parent,rec.pr[grp[level]])];
			if(!ics.idx) ics.idx=++idx;
			ics.sumary+=rec.cs;
			parent=ics.idx;
		}		
	}
}

void printtree(ostream &xout,tree &tr,size_t level,size_t parent)
{
	xout<<fixed;
	tree::iterator begin=tr.lower_bound(key(parent));
	tree::iterator end=tr.lower_bound(key(parent+1));
	for(tree::iterator i=begin;i!=end;++i)
	{
		if(level>0) xout<<setw(level)<<' ';
		const string &title=i->first.value;
		xout<<title<<setw(20-level-title.size())<<' ';
		xout<<setw(12)<<setprecision(2)<<i->second.sumary.count;
		xout<<setw(12)<<setprecision(2)<<i->second.sumary.price;
		xout<<setw(12)<<setprecision(2)<<i->second.sumary.amount;
		xout<<endl;
		printtree(xout,tr,level+3,i->second.idx);
	}
}

void groupby(ostream &xout,const datasource &data,const group &grp)
{
	tree tr;
	maketree(tr,data,grp);
	printtree(xout,tr,0,0);
	xout<<"==============================="<<endl<<endl;
}

datasource bigdata
{
	{cash(5,2),{"55","marchewka","kg","Asia"}},
	{cash(3,5),{"55","sliwki","kg","Kasia"}},
	{cash(3,100),{"67","buty","szt","Asia"}},
	{cash(4,70),{"67","marki","szt","Kasia"}},
	{cash(5,20),{"67","gumowiec","szt","Jola"}},
	{cash(6,20),{"12","pasta","szt","Asia"}},
	{cash(5,10),{"12","do","szt","Jola"}},
	{cash(4,5),{"12","nosa","szt","Basia"}},
};

int main()
{
	groupby(cout,bigdata,{2,0,3});
	groupby(cout,bigdata,{0,2});
	groupby(cout,bigdata,{2,0,1});
	return 0;
}

1 użytkowników online, w tym zalogowanych: 0, gości: 1