задачка 2.
*как выяснилось, решение корректно на числах одного порядка
есть 10 миллиардов записей в файле, каждая запись это число типа double, требуется найти минимальное значение из 10% максимальных.
мое решение таково, создаем хеш ключом которого будет первые n символов числа, а значением, кол. вхождения этого ключа в файле. Сортируем по ключам и отсчитываем в каком ключе будет начало 10%, проходим еще раз массив значений и выбираем те которые соответствуют ключу, сортируем его, и вытаскиваем позицию соответствующую 10%.
# запуск perl begin.pl FILE DL RESULT # , где # FILE файл который требуется обработать # DL длинна ключа для групп (1..n, для 100кк записей достаточно 5) # RESULT финальный шаг, 1 - включен, 2 - не включен, тогда после отработки begin.pl, нужно запустить result.pl (perl result.pl) - не актуально, скрипт выполняется полностью независимо от этого параметра. # если в программу не переданы параметры, скрипт запрашивает их сам. # Алгоритм: # ШАГ 1. # разбиваем весь массив на группы и заносим их в хеш массив, ключом будет уникальная последовательность первых символов строки длинной DL , значением количество совпадений этого ключа встреченное по проходу файла. # (Чем больше DL тем больше групп, и тем меньше в этих группах значений, сортируются как группы так и ключи, так что важен баланс, чтобы оба шага отрабатывали за оптимальное время.) # ШАГ2. # зная количество строк в файле, получаем длину искомых 10%, сортируем по ключу хеш, и определяем группу в которой будет находится начальное значение наших 10% # ШАГ3. # проходим еще раз файл и отбираем значения подходящие под ключ определенной ранее группы, записываем эту группу в файл, на всякий случай. # ШАГ4. # сортируем файл со значениями группы, зная позицию где должно находится начало 10% извлекаем нужное значение. # Итог. # приходится 2 раза обходить БОЛЬШОЙ файл, на 10^8 значениях вида хх.уууууууууу, где хх от 1 до 15, а у от 0 до 9, время работы скрипта при DL=5 было 3 минуты. # в папке group находится результат шага3 # begin.pl - шаги с 1 по 3. # result - шаг 4. my $file,$dl,$r; if (defined(@ARGV)){ ($file,$dl,$r)=@ARGV; print "File: ".$file."\n"; if (!defined($dl) || $dl==0){$dl=4;} print "Quantity of signs in a group key: ".$dl."\n"; if (defined($r) and $r!=0){$rs="yes";}else{$rs="no";} print "To start 4 step? ".$rs."\n\n"; }else{ ($file,$dl,$r)=prepareToStart(); } sub prepareToStart{ print "Please, input path and filename (ex. /log/file.txt): "; my $file=<>;$file=~s/\n//g; print "\nEnter length of a key (ex. 5): "; my $dl=<>;$dl=~s/\n//g; if ($dl<=0){$dl=5;} print "The result press? - yes\n"; my $r=1; return ($file,$dl,$r); } if (!-e $file){print "file \"$file\" not found, exit";exit;} print "STEP 1\n"; my ($d10,%group)=first($file,$dl); print "Quantity of values in 10 % (D10): ".$d10."\n"; print "...ok\n"; print "STEP 2\n"; my ($key,$position,$c)=&second(\$d10,\%group); my $cGroup=keys(%group); print "Groups: $cGroup, Group: $key, Position: $position Elements: $c\n"; print "...ok\n"; print "STEP 3\n"; my $tempFile=third($key,$position,$file); print "...ok\n"; print "STEP 4\n"; &analiz($tempFile); print "...ok\n"; sub analiz{ my $file=shift; if (-e $file){ if ($file=~m/^.+_(\d+)$/){ analizGroup($file,$1); } } } sub analizGroup{ my ($file,$position)=@_; open (F, "$file") || die ("$file $!"); my $fl="",@m=(); while($fl=<F>){ $fl=~s/\n//g; push(@m,$fl); } close F; sortGroup(\@m); print "The minimum value from 10 % is ".$m[$position-1]."\n"; unlink($file); } sub sortGroup{ my $ref=shift; @$ref=sort{$b<=>$a}(@$ref); } sub first{ my ($file,$dl)=@_; open (FL, $file) || die ("$file $!"); my $i=0,$fl,@keys,$d10; my %group; my $regexp=".{0,$dl}"; while($fl=<FL>){ $i++; if ($fl=~m/(^(??{$regexp}))/){ $group{$1}++; } } close FL; # количество элементов в 10% $d10=int($i*0.1); #возвращаем размер 10%, и хеш{группа}=кол элементов. return ($d10,%group); } sub second{ my ($r1,$r2)=@_; my @st=sort{$b<=>$a}(keys(%{$r2})); my $counter=0; foreach my $key (@st){ $counter+=${$r2}{$key}; if ($counter>=${$r1}){ my $position=${$r2}{$key}-($counter-${$r1}); #возвращаем ключ нужной группы, искомую позицию и количество элементов в группе. return ($key,$position,${$r2}{$key}); } } } sub third{ my ($key,$position,$file)=@_; open (FL, $file) || die; open (OUT, "> ${key}_${position}") || die ("${key}_${position} $!"); my $i,$fl,@keys,$c=0; while($fl=<FL>){ if ($fl=~m/^(??{$key})/o){ $i++;$c++; push(@keys,$fl); if ($i>=100){ print OUT @keys; undef(@keys); $i=0; } } } if (defined(@keys) && $#keys+1>0){ print OUT @keys; undef(@keys); } close OUT; close FL; return ("${key}_${position}"); }
Комментарии закрыты.