【问题标题】:Bin evenly: remainder spacing is unevenbin evenly:余数间距不均匀
【发布时间】:2011-10-18 17:10:53
【问题描述】:

我正在编写一个脚本来将任意数量的$epi 均匀地分箱到任意数量的箱 $dpi 中。 Epi 代表每英寸端数。 dpi 表示每英寸的凹痕数。有3个要求:

  • 如果可能的话,bin 数应该减少最小的公因数
    • 例如6 dpi 中的 10 个 Epi 应由 3 dpi 中的 5 个 Epi 表示
  • bin 数量应尽可能统一
    • 例如2-2-1 优于 3-1-1
  • 短 bin 应均匀分布在 bin 阵列中
    • 例如1-0-1-0-1 优于 1-1-1-0-0

这是我目前所拥有的。它主要做我需要的但是space() 方法运行时,如果它的foreach 循环必须执行多次,$epi 的分布不均匀。

<?php
class ReedSubstitution{

    public $epi;
    public $dpi;

    function substitute($e,$d){
        $this->epi=is_numeric($e)?$e:0;
        $this->dpi=is_numeric($d)?$d:0;
        if(empty($this->epi)||empty($this->dpi)) throw new Exception('Both desired ends per unit and available dents per unit must be specified.');

        if($this->epi%$this->dpi ==0) return array($this->epi/$this->dpi);//short circuit for easy case
        $this->unfraction();//make equivalent integers if dpi or epi are fractional
        $gcd= ($this->epi < $this->dpi) ? $this->euclid($this->epi,$this->dpi) : $this->euclid($this->dpi,$this->epi);
        $e=$this->epi/$gcd;
        $d=$this->dpi/$gcd;

        $q=floor($e/$d);//count that every dent gets
        $r=$e%$d;//extra to be spread out over array
        $reed=array_fill(0,$d,$q);
        $this->space($reed,$r);
        return $reed;
    }

protected function unfraction(){
    $emult=1;
    $dmult=1;
    if($this->epi-intval($this->epi)){ //epi has a fractional component
        list($tmp,$er)=explode('.',$this->epi);
        $emult=pow(10,strlen($er));
    }
    if($this->dpi-intval($this->dpi)){//dpi has a fractional component
        list($tmp,$dr)=explode('.',$this->dpi);
        $dmult=pow(10,strlen($dr));
    }
    $mult=($emult>$dmult)?$emult:$dmult;
    $this->epi*=$mult;
    $this->dpi*=$mult;
}

/**
 * @desc  evenly distribute remaining ends over entirety of dents
 * @param Array $reed, dents in one pattern repeat
 * @param Integer $r, remaining ends to be distributed
 */
protected function space(&$reed,$r){
    $c=count($reed);
    $i=0;
    $jump=($r < $c-$r) ? $r : $c-$r;//use the smallest jump possible to reduce the looping
    do{
        for($i; $i<$c; $i=$i+$jump, $r--){
            if($r==0)break;
            $reed[$i]++;
        }
        $i=array_search(min($reed),$reed);//begin next loop (if necessary) at position with fewest ends
    }while($r>0);
}    
    /**
     * @desc return greatest common divisor as determined by Euclid's algorithm
     * @param integer $large
     * @param integer $small
     * @return integer
     */
    protected function euclid($large, $small){
        $modulus=$large%$small;
        if($modulus==0) {
            return $small;
        } else if($modulus==1){
            return 1;
        } else {
            return $this->euclid($small,$modulus);//recursion
        }
    }
}
?>

错误的输出:

$r=new ReedSubstitution();

$arr=$r->substitute(9,28);
/* BAD DISTRIBUTION
Array
(
[0] => 1
[1] => 1
[2] => 1
[3] => 0
[4] => 0
[5] => 0
[6] => 0
[7] => 0
[8] => 0
[9] => 1
[10] => 1
[11] => 1
[12] => 0
[13] => 0
[14] => 0
[15] => 0
[16] => 0
[17] => 0
[18] => 1
[19] => 1
[20] => 0
[21] => 0
[22] => 0
[23] => 0
[24] => 0
[25] => 0
[26] => 0
[27] => 1
)
*/

上面的分布应该是什么样子:

/* GOOD DISTRIBUTION
Array
(
[0] => 1
[1] => 0
[2] => 0
[3] => 1
[4] => 0
[5] => 0
[6] => 1
[7] => 0
[8] => 0
[9] => 1
[10] => 0
[11] => 0
[12] => 1
[13] => 0
[14] => 0
[15] => 1
[16] => 0
[17] => 0
[18] => 1
[19] => 0
[20] => 0
[21] => 1
[22] => 0
[23] => 0
[24] => 1
[25] => 0
[26] => 0
[27] => 0
)
*/

如何修复space() 方法,以便需要多个循环的分箱产生可接受的分布?

【问题讨论】:

  • @Jonathan Chan:这不是家庭作业。我正在为一个编织小组开发一个网站。他们想要一个芦苇替换小部件,这样他们就不必使用这样的图表:joowl.com/reedsubs.html 我可以将图表存储在数据库中并提取记录,但在我看来这应该是一个易于处理的数学问题。
  • 对我的问题投反对票的人,我们将不胜感激。我认为问题很清楚,并且有足够的代码和输出,因此读者可以看到我遇到的问题。

标签: php arrays binning


【解决方案1】:

我希望这会有所帮助或至少为您指明正确的方向:

protected function space(&$reed, $r)
{
    $totalReeds = count($reed);

    $f = floor($r/$totalReeds);
    $d = ($r % $totalReeds) / $totalReeds;
    $c = 0;

    for ($i = 0; $i < $totalReeds; $i++)
    {
        $add = round($f + $d + $c);
        $reed[$i] += $add;
        $c = $c + $f + $d - $add;
    }
}

然而,它可能产生的结果并非完全你所期望的:

Array
(
    [0] => 2
    [1] => 1
    [2] => 2
    [3] => 2
    [4] => 1
    [5] => 2
    [6] => 1
    [7] => 2
)

虽然结果是均匀分布。

附:我不太了解您处理的与网站相关的实际问题,但我希望我掌握了正确的数学概念。

【讨论】:

    【解决方案2】:

    你可以试试下面的空间函数吗?

    protected function space(&$reed, $r)
    {
            $totalReeds = count ($reed);
    
            $postion = floor($totalReeds/$r);
    
            for ($i=0; $i<=$totalReeds; $i=$i+$postion, $r--) {
                    if ($r <= 0) break;
                    $reed[$i]++;
            }    
    } 
    

    我刚刚尝试了一些输入,它对我有用。此外,它为我提供了您之前给出的示例的正确输出。

    您能否尝试使用此功能,让我知道它是否适用于您的所有输入。

    -- 编辑--

    试试这个代码。

        protected function space(&$reed, $r)
        {
                $totalReeds = count ($reed);
    
                $postion = floor($totalReeds/$r);
    
                $postion = $postion == 1? 2 : $postion;
    
                for ($i=0; $i<=$totalReeds; $i=$i+$postion, $r--) {
                        if ($r <= 0) break;
    
                        if (isset($reed[$i])) $reed[$i]++;
                }    
        }     
    

    我希望这有效。我不确定您在评论中所说的其他输入的预期结果。所以我假设以下是正确的。如果我错了,请发布其他输入的预期结果。

    http://ideone.com/L4fUv

    问候,

    【讨论】:

    • 不幸的是,它只适用于某些输入。使用space() 函数查看此运行:ideone.com/YHF6g 您会看到第二个和第四个示例分布不均。谢谢
    • 第二个和第四个示例的预期输出是什么?
    • 第二个例子(13epi over 8 dpi)应该是2-2-1-2-2-1-2-1你的给2-2-2-2-2-1-1-1。第 4 个例子太长了,放在这里,但是 1 和 0 应该均匀地穿插而不是所有 1 的块,然后是所有 0 的块。
    【解决方案3】:

    哇,这段代码太可怕了,恕我直言 :) 递归查找公约数,is_numeric 和 intval 的疯狂使用,一个孤独的例外等等。类架构也很奇怪,我会把它作为静态类来做。

    老实说,我没有得到关于它的数学问题,因为我发现的所有关于垃圾箱和凹痕的东西都在编织(我不是母语人士,所以我可能会错过一些明显的东西),但我想我理解你的要求(如果这不是一个严格的数学问题)。

    无论如何,我清理了一下,所以我发布了完整的类代码,但如果你不喜欢它,你可以单独使用 space()。我的算法可以优化很多(第二个循环可以去掉),但是我懒得做。

    class ReedSubstitution {
    
        private $epi;
        private $dpi;
    
        public function substitute($e, $d) {
            $this->epi = floatval($e);
            $this->dpi = floatval($d);
    
            if (empty($this->epi) || empty($this->dpi)) {
                throw new Exception('Both desired ends per unit and available dents per unit must be specified.');
            }
    
            //make equivalent integers if dpi or epi are fractional
            $this->unfraction();
    
            if ($this->epi % $this->dpi == 0) {
                return array($this->epi / $this->dpi);
            }
    
    
            $gcd = $this->euclid($this->epi, $this->dpi);
    
            $e = $this->epi / $gcd;
            $d = $this->dpi / $gcd;
    
            $r = $e % $d; //extra to be spread out over array
            $q = ($e - $r) / $d; //count that every dent gets
    
            $reed = array_fill(0, $d, $q);
            $this->space($reed, $r);
            return $reed;
        }
    
        protected function unfraction() {
            //Find fraction start position
            $epi_fract_pos = strpos($this->epi, '.');
            $dpi_fract_pos = strpos($this->dpi, '.');
            //Find fraction length
            $epi_fract_len = $epi_fract_pos ? (strlen($this->epi) - $epi_fract_pos - 1) : 0;
            $dpi_fract_len = $dpi_fract_pos ? (strlen($this->dpi) - $dpi_fract_pos - 1) : 0;
            //Calculate max fraction length
            $fraction_len = max($epi_fract_len, $dpi_fract_len);
            //Unfraction variables
            $mult = pow(10, $fraction_len);
            $this->epi*=$mult;
            $this->dpi*=$mult;
        }
    
        /**
         * @desc  evenly distribute remaining ends over entirety of dents
         * @param Array $reed, dents in one pattern repeat
         * @param Integer $r, remaining ends to be distributed
         */
        protected function space(&$reed, $r) {
            $c = count($reed);
            $base = $reed[0];
            for ($i = 0; $i < $r; $i++) {
                $reed[$i]++;
            }
    
            while ($reed[$c - 1] === $base) {
                $reed[$c] = $base;
                //Find the longest $base+1 array with least $base behind it
                $max_base_plus_size = -$c;
                $cur_base_plus_size = 0;
                $cur_base_plus_pos = array(NULL, NULL);
                $max_base_plus_pos = NULL;
    
                for ($i = 0; $i <= $c; $i++) {
                    if ($reed[$i] != $base) {
                        if($cur_base_plus_pos[0]===NULL) {
                                $cur_base_plus_pos[0] = $i;
                        }
                        if ($reed[$i + 1] == $base) {
                            $cur_base_plus_pos[1]=$i;
                            if ($max_base_plus_size < $cur_base_plus_size) {
                                $max_base_plus_size = $cur_base_plus_size;
                                $max_base_plus_pos = $cur_base_plus_pos;
                            }
                            $cur_base_plus_size = 0;
                            $cur_base_plus_pos = array(NULL, NULL);
                        }
                        else {
                            $cur_base_plus_size++;
                        }
                    } else {
                        $cur_base_plus_size--;
                        $cur_base_plus_pos[1] = $i - 1;
                    }
                }
                $insert_pos=ceil(($max_base_plus_pos[1]+$max_base_plus_pos[0])/2);
                array_splice($reed,$insert_pos,0,$base);
            }
            array_splice($reed,$c);
            $reed=array_reverse($reed);//Optional, just to get the same output as you submitted
        }
    
        /**
         * @desc return greatest common divisor as determined by Euclid's algorithm
         * @param integer $x
         * @param integer $y
         * @return integer
         */
        protected function euclid($x, $y) {
            while ($x) {
                $temp = $x;
                $x = $y % $x;
                $y = $temp;
            }
            return $temp;
        }
    
    }
    

    【讨论】:

    • array_splice($reed,$insert_pos,0,$base); 抛出错误“array_splice() 期望参数 1 为数组,给定整数”
    • @dnagirl:嘿,输入数据是什么?对我来说一切都很好。测试来自 ideone.com/YHF6g 的数据。您是单独使用我的课程还是仅使用 space() 函数?示例:ideone.com/sP0OF(相同的数据,但我的班级)。
    猜你喜欢
    • 2020-10-05
    • 1970-01-01
    • 1970-01-01
    • 2017-01-31
    • 1970-01-01
    • 1970-01-01
    • 2017-08-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多