【问题标题】:PHP OpenDir vs array_randPHP OpenDir 与 array_rand
【发布时间】:2018-03-20 19:36:28
【问题描述】:

我有一个图像目录,其中可能包含 100 到数千张图像。我需要从这个目录中抽取 81 个随机图像的样本来使用(在一个数组中)。

我目前正在使用以下方法来抓取图像

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

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

我还看到可以使用 opendir 然后对数组进行洗牌。有人可以告诉我哪个更有效吗?我会假设使用 shuffle 然后抓取前 81 个元素会更好,但对于更大的计数会更慢(因为 shuffle 大数组需要更长的时间)。

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

谢谢

【问题讨论】:

  • 为什么不对它们进行测试和计时?
  • @AbraCadaver 因为如果我不需要,我不想将数千张图像上传到我的服务器来测试这一点
  • 在本地测试它也可以给你一个很好的提示。好吧,至少,如果您使用操作系统和相同的文件系统。顺便问下好问题。

标签: php time-complexity


【解决方案1】:

这是一个非常好的问题,我希望能提出更多这样的问题。


$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 循环将运行更长时间,因为在较小的样本上碰撞的变化更高。

【讨论】:

  • @Andrew 谢谢你,我认为数字生成器绝对是一种更好的方法,很高兴你有文件系统来测试它。幸运的是,就我的应用程序而言,目录中的文件永远不会少于 81 个,因此警告(inf. for 循环)无关紧要:)
  • @Andrew array_intersect_key 在 PHP 5.3 中的工作方式不同吗?我将 random_int 切换为 rand 但 $final 始终是完全相同的文件(random_array 不同,因此它不能抓取散列或类似的东西)
  • 哎呀,你是完全正确的。我的坏,是赶时间并没有真正想到这一点。我已经编辑了这个问题。您可以在 for 循环中添加文件,而不是组合数组。检查数组键是否存在,如果不添加,就是这样。如果您需要按顺序排列密钥,请在 $random_array 上执行 array_values,然后 ti 将对其进行重新排序。由于它只有 81 个元素,因此性能不会成为问题。
猜你喜欢
  • 1970-01-01
  • 2014-11-06
  • 2012-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多