2017-10-09 40 views
3

我有一个图像的目录,这可能包含从100到成千上万的图像的任何地方。我需要从这个目录中取出81个随机图像的样本(在一个数组中)。PHP的OpenDir vs array_rand

我目前使用下面的抢图像

$locations = 'compressed/'; 
$images = glob($locations . '*', GLOB_BRACE); 
$selected = $images[array_rand($images)]; 

这种方法的问题是,它是有可能得到相同的图像两次(尽管大样本很少)

我有也看到opendir可以用来洗牌阵列。有人可以告诉我哪个更有效吗?我会假设使用shuffle,然后抓住第81个元素会更好,但对于更大的计数会更慢(因为洗牌大型阵列需要更长的时间)。

关于我当前设置的时间复杂度的任何建议,而不是使用opendir(或其他我可能不知道的方法)?

谢谢

+0

为什么不测试和计时? – AbraCadaver

+0

@AbraCadaver因为我不想上传成千上万的图像到我的服务器来测试这个,如果我没有 –

+1

本地测试可以给你一个很好的提示。至少,如果你使用操作系统和相同的文件系统。顺便问一下好问题。 – iquellis

回答

2

这是一个非常好的问题,我希望更多这些会出现。


$start = microtime(true); 

function recursiveDirectoryIterator($path) { 
    foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path)) as $file) { 
    if(!$file->isDir()) { 
     yield $file->getFilename() . $file->getExtension(); 
    } 
    } 
} 

$instance = recursiveDirectoryIterator('../vendor'); 
$files = []; 
foreach($instance as $value) { 
    $files[] = $value; 
} 

$total_files = count($files); 
$random_array = []; 
$total_randoms = 81; 
for(;;){ 
    $rand = random_int(0, $total_files); 
    if(count($random_array) == $total_randoms) { 
    break; 
    } 
    if(!isset($random_array[$rand])) { 
    $random_array[$rand] = $files[$rand]; 
    } 
} 

echo "Mem peak usage: " . (memory_get_peak_usage(true)/1024/1024)." MiB" . '<br>'; 
echo "Total number of files: " . $total_files . '<br>'; 
echo "Completed in: ", microtime(true) - $start, " seconds" . '<br>'; 
echo '<pre>'; 
print_r($final); 
die; 

输出

Mem peak usage: 2 MiB 
Total number of files: 12972 
Completed in: 0.74663186073303 seconds 
Array 
(
    [6118] => PreDec.phpphp 
    [4560] => LabelMaker.phpphp 
    [10360] => RecursiveDirectoryIterator.phpphp 
    [4124] => Enum.phpphp 
    [2671] => ImportCommand.phpphp 
    [1250] => WebDriverTest.phpphp 
    [10518] => AutoExpireFlashBagTest.phpphp 
    [6805] => zsdtPackTask.phpphp 
    [4288] => HTML.Trusted.txttxt 
    [6462] => border-disable.phptphpt 
    [4980] => main.ymlyml 
    [505] => StepTested.phpphp 
    [5219] => xhprof.ini.j2j2 
    [12959] => RequestInterface.phpphp 
    [1423] => xd5.phpphp 
    [4285] => HTML.TidyAdd.txttxt 
    [4930] => .travis.ymlyml 
    [12013] => Defined.phpphp 
    [8779] => Markdown.phpphp 
    [5979] => pt.phpphp 
    [278] => AbstractAdapter.phpphp 
    [2155] => SemVerTest.phpphp 
    [523] => ServicesResolverFactory.phpphp 
    [11686] => AbstractDumper.phpphp 
    [7320] => Functions.phpphp 
    [7763] => mocked_clone.tpl.distdist 
    [11541] => test_landscape.gifgif 
    [3557] => RegionSelectorSpec.phpphp 
    [2600] => RoutingAccessSniff.phpphp 
    [9496] => LoaderTest.phpphp 
    [4958] => setup-RedHat.ymlyml 
    [3477] => api.featurefeature 
    [7975] => WtfCommand.phpphp 
    [9001] => ElseIfDeclarationSniff.phpphp 
    [11696] => VarDumperTestTrait.phpphp 
    [11211] => empty.ymlyml 
    [10925] => ObjectRouteLoader.phpphp 
    [10936] => MatcherDumperInterface.phpphp 
    [2685] => ConnectCommand.phpphp 
    [9066] => EmptyStyleDefinitionSniff.phpphp 
    [3536] => BehatTestExtensionInstallStorage.phpphp 
    [4720] => ansible-args.mdmd 
    [326] => ZipOutputParser.phpphp 
    [9565] => BufferedOutput.phpphp 
    [712] => CliExtension.phpphp 
    [3436] => .travis.ymlyml 
    [4471] => HTMLPurifier.kses.phpphp 
    [2764] => RouteSubscriberCommand.phpphp 
    [10633] => RoutableFragmentRenderer.phpphp 
    [6906] => Reference.phpphp 
    [11663] => DoctrineCaster.phpphp 
    [8042] => GitHubChecker.phpphp 
    [1466] => ImageDriverInterface.phpphp 
    [2652] => DrupalCommand.phpphp 
    [7265] => classUsesNamespacedFunction.phpphp 
    [12129] => ExtensionInterface.phpphp 
    [12184] => ConditionalExpression.phpphp 
    [12128] => EscaperExtension.phpphp 
    [6678] => JsHintTask.phpphp 
    [5351] => main.ymlyml 
    [2104] => _bootstrap.phpphp 
    [143] => deploy_branch 
    [1360] => x8f.phpphp 
    [4713] => composer-dependency.mdmd 
    [7495] => ExceptionInAssertPostConditionsTest.phpphp 
    [4508] => info.txttxt 
    [8369] => 6.1.3-curl-adapter.phpphp 
    [3093] => create-data.ymlyml 
    [1882] => .gitkeepgitkeep 
    [3747] => example.makemake 
    [507] => EventDispatchingBackgroundTester.phpphp 
    [3336] => shell.ymlyml 
    [397] => AnnotationReader.phpphp 
    [4005] => xhUnitTest.phpphp 
    [5168] => test.ymlyml 
    [10909] => MissingMandatoryParametersException.phpphp 
    [8686] => FacetSetTest.phpphp 
    [2321] => FileCache.phpphp 
    [10538] => StreamedResponseTest.phpphp 
    [12572] => in.testtest 
    [7031] => StringContainsToken.phpphp 
) 

代码打破。

我用RecursiveDirectoryIteratorGenerator来节省内存使用量。

接下来,我并没有重新排序一个巨大的数组,而是选择了另一种方法:在文件数组的最大数量和0的范围内生成81个随机的,非重复的数字。一旦你有了随机数,只需使用array_intersect_key快速。

待办事项这是我没有考虑到一个逻辑陷阱:

  • 如果文件的总数量小于81,for循环将永远运行下去。


最后说明一点:我绝对肯定有人比我能想到的东西更好更聪明,但到目前为止,这会工作。

另外,由于我使用的是PHP 7.x,我有opcache的优势,性能会更好,您的结果可能会有所不同。

请注意,如果文件的数量是非常少的for循环会更长时间运行,因为碰撞的变化是更小的样品更高。

+0

@安德鲁感谢你,我认为数字生成器绝对是一个更好的方法,并且很高兴你有文件系统来测试它。幸运的是,为了我的应用程序的目的,永远不会少于目录中的81个文件,以便警告(inf。for循环)无关紧要。:) –

+1

@Andrew在array_intersect_key中的工作方式与PHP 5.3不同吗?我将random_int切换为rand,但$ final总是完全相同的文件(random_array是不同的,所以它不能抓取哈希或类似的东西) –

+0

Ooops,你是绝对正确的。我的坏,急着没有真正认为一个。我编辑了这个问题。您可以在for循环中添加文件,而不是组合数组。检查数组键是否存在,如果它没有添加它,就是这样。如果你需要按键顺序,那么在'$ random_array'上执行'array_values',ti会对它重新排序。由于它只有81个元素,所以性能不会成为问题。 – Andrew