欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

逆FizzBuzz问题求最短序列

程序员文章站 2022-04-28 09:22:35
问题描述 FizzBuzz问题:一个大于0的自然数能整除3,将输出“Fizz”;能整除5,将输出“Buzz”;能整除3和5,将输出“FizzBuzz”;否则输出自己。 逆FizzBuzz问题最短序列:已知一个FizzBuzz问题的非数字输出序列,求能获得该序列的最短连续数字序列。如“Fizz”的最短 ......

问题描述

FizzBuzz问题:一个大于0的自然数能整除3,将输出“Fizz”;能整除5,将输出“Buzz”;能整除3和5,将输出“FizzBuzz”;否则输出自己。

逆FizzBuzz问题最短序列:已知一个FizzBuzz问题的非数字输出序列,求能获得该序列的最短连续数字序列。如“Fizz”的最短序列是“3”,“Fizz Buzz”的最短序列是“9 10”,而不是“3 4 5”。

 

编程实现

  1 <?php
  2 
  3 /**
  4  * @author cenze
  5  *
  6  * 反FizzBuzz问题求最短序列
  7  * 
  8  * $inverseFizzBuzz = new InverseFizzBuzz([
  9  *   'fizz',
 10  *   'fizz',
 11  *   'buzz',
 12  * ]);
 13  * $inverseFizzBuzz->sequence():
 14  * Array ( [0] => 6 [1] => 7 [2] => 8 [3] => 9 [4] => 10 )
 15  */
 16 class InverseFizzBuzz
 17 {
 18     // FizzBuzz问题赋值
 19     const FizzBuzz = [
 20         'fizz' => 3,
 21         'buzz' => 5,
 22         'fizzbuzz' => 15
 23     ];
 24     
 25     // 预定义问题区间
 26     private $range = [
 27         1,
 28         100
 29     ];
 30     
 31     // 待求序列
 32     private $list = [];
 33     
 34     // 待求序列包含项数统计
 35     private $listItemsCount = 0;
 36 
 37     public function __construct($list = [], $range = [])
 38     {
 39         $this->init($list, $range);
 40     }
 41 
 42     /**
 43      * 待求序列、区间初始化
 44      *
 45      * @param array $list
 46      *            待求序列
 47      * @param array $range
 48      *            问题区间
 49      * @param bool $check
 50      *            是否检查属性已完成初始化
 51      *            
 52      * @return void
 53      */
 54     private function init($list = [], $range = [], $check = false)
 55     {
 56         if ($list) {
 57             $this->list = $this->inverseList($list);
 58             $this->listItemsCount = count($this->list);
 59             if (! $this->listItemsCount) {
 60                 exit('Invalid list.');
 61             }
 62         }
 63         
 64         if ($range) {
 65             $this->range = $range;
 66         }
 67         
 68         if ($check) {
 69             if (! $this->list) {
 70                 exit('Property list uninitialized.');
 71             }
 72         }
 73     }
 74 
 75     /**
 76      * 将待求解序列反转
 77      *
 78      * @param array $list
 79      *            待求解序列
 80      *            
 81      * @return mixed
 82      */
 83     private function inverseList($list)
 84     {
 85         $inverseList = [];
 86         foreach ($list as $item) {
 87             if (! array_key_exists($item, self::FizzBuzz)) {
 88                 return null;
 89             }
 90             $inverseList[] = self::FizzBuzz[
 91                 $item
 92             ];
 93         }
 94         
 95         return $inverseList;
 96     }
 97 
 98     /**
 99      * 搜索最短序列是否存在
100      *
101      * @param array $list
102      *            待求序列
103      * @param array $range
104      *            问题区间
105      *            
106      *            
107      * @return mixed
108      */
109     public function sequence($list = [], $range = [])
110     {
111         $this->init($list, $range, true);
112         
113         $sequences = $this->findSequences();
114         if (! $sequences) {
115             return 'Found no sequences.';
116         }
117         
118         $sequence = $this->shortestSequence($sequences);
119         // 将最短序列序列化后返回
120         return range($sequence[0], $sequence[$this->listItemsCount - 1]);
121     }
122 
123     /**
124      * 搜索最短序列(未序列化)
125      *
126      * @param array $sequences
127      *            序列集合,包含最短序列
128      *            
129      * @return array
130      */
131     private function shortestSequence($sequences)
132     {
133         // 默认第一条为最短
134         $shortestSequence = $sequences[0];
135         // 把每一个序列看做一条路径,都有对应的长度。记录最短的序列长度
136         $shortestSequenceLength = $sequences[0][$this->listItemsCount - 1] - $sequences[0][0];
137         // 为array_reduce()定义一个callback,用来搜索最短序列
138         $findShortestSequence = function ($shortestSequence, $currentSequence) use(&$shortestSequenceLength) {
139             $currentSequenceLength = $currentSequence[$this->listItemsCount - 1] - $currentSequence[0];
140             if ($currentSequenceLength < $shortestSequenceLength) {
141                 $shortestSequenceLength = $currentSequenceLength;
142                 return $currentSequence;
143             } else {
144                 return $shortestSequence;
145             }
146         };
147         
148         return array_reduce($sequences, $findShortestSequence, $shortestSequence);
149     }
150 
151     /**
152      * 从指定位置开始搜索指定项目的序列
153      *
154      * @param int $item
155      *            指定项目
156      * @param int $start
157      *            起始搜索位置
158      *            
159      * @return int
160      */
161     private function findItemSequenceFromPosition($item, $start)
162     {
163         if ($start % $item == 0) {
164             return $start;
165         }
166         
167         return $start + ($item - $start % $item);
168     }
169 
170     /**
171      * 从指定位置开始搜索首个序列
172      *
173      * @param int $start
174      *            起始搜索位置
175      *            
176      * @return mixed
177      */
178     private function findSequenceFromPosition($start)
179     {
180         $listSequence = [];
181         for ($i = 0; $i < $this->listItemsCount; $i ++) {
182             $itemSequence = $this->findItemSequenceFromPosition($this->list[$i], $start);
183             if ($itemSequence > $this->range[1]) {
184                 return null;
185             } else {
186                 $listSequence[] = $itemSequence;
187                 $start = $itemSequence + 1;
188             }
189         }
190         
191         return $listSequence;
192     }
193 
194     /**
195      * 找到多个序列,其中包括最短序列
196      *
197      * @return void
198      */
199     private function findSequences()
200     {
201         $sequences = [];
202         // 以第一个项为初始搜索位置在预定区间中搜索,且以第一个项为递增步长向后逐步搜索
203         for ($start = $this->list[0]; $start <= $this->range[1]; $start += $this->list[0]) {
204             // 只需找到指定位置开始的第一个序列即可
205             if ($sequence = $this->findSequenceFromPosition($start)) {
206                 $sequences[] = $sequence;
207             }
208         }
209         
210         return $sequences;
211     }
212 }