Задача: Найти сумму натуральных чисел кратных 3 и 5 до предела в 1000

28.12.2019

Описание проблемы

Оригинальная задача и описание по этой ссылке: https://projecteuler.net/problem=1

Перевод от гугл:

Если мы перечислим все натуральные числа ниже 10, кратные 3 или 5, мы получим 3, 5, 6 и 9. Сумма этих кратных равна 23. Найти сумму всех кратных 3 или 5 ниже 1000.

Результат

Сразу начну с результата, это важно потому что я сделал объектную реализацию этой задачи. Вот код который рассчитывает сумму чисел которые кратны 3 и 5 и меньше 1000

$orderGenerator = new OrderListGenerator(1000);
$numbersList = $orderGenerator->build();
$multiplesFinder = new MultiplesFinder($numbersList);
$multiplesList = $multiplesFinder->find();
$sum = new NumbersSum($multiplesList);

echo "\n";
echo $sum;
echo "\n";

Я сразу начал с результата, чтобы показать, что объектный код очень легко читается и им легче пользоваться чем мешаниной из условий и циклов. с помощью этого кода можно легко посчитать результат и для пределов в 10 000 и для 100 000. Далее разберем реализацию каждого класса.

Классы

Number

Объектное представление одного числа. хранит в себе только значение этого числа в свойстве $value. Для упрощения $value объявлен как public, но в более сложной программе, лучше закрыть прямой доступ к этому свойству и объявить его как private.

class Number
{
   public $value;
   public function __construct($value)
   {
      $this->value = $value;
   }
   public function __toString()
   {
      return $this->value;
   }
}

ListOfNumbers:

Объектное представление набора чисел. Такой подход полезен для типизации передаваемых данных в аргументах методов. То есть вместо не известного массива я использую этот тип, это позволяет мне быть уверенным в том что я буду работать именно с набором объектов Number.

class ListOfNumbers
{
  /** @var Number[] */
  private $list = [];
  public function add(Number $number)
  {
    $this->list[] = $number;
  }
  /**
   * @return Number[]
   */
  public function iterator(): iterable
  {
    foreach ($this->list as $number) {
      yield $number;
    }
  }
}

OrderListGenerator

Данный класс просто генерирует набор чисел (ListOfNumbers) по алгоритму от 1 до $limit. В конструктор передается аргумент $limit.

class OrderListGenerator
{
  private $limit;
  public function __construct($limit = 10)
  {
    $this->limit = $limit;
  }
  public function build(): ListOfNumbers
  {
    $list = new ListOfNumbers();
    for ($i=1; $i< $this->limit;$i++) {
      $list->add(new Number($i));
    }
    return $list;
  }
}

NumberSum

Объект представляющий сумму всех чисел (Number) внутри списка чисел (ListOfNumbers). Метод __toString() позволяет сразу вывести объект этого класса на экран echo $sum; где $sum объект класса NumberSum.

class NumbersSum
{
  private $sum;
  public function __construct(ListOfNumbers $listOfNumbers)
  {
    $sum = 0;
    foreach ($listOfNumbers->iterator() as $number) {
      $sum += $number->value;
    }
    $this->sum = $sum;
  }
  public function __toString()
  {
    return "Sum is $this->sum";
  }
}

MultiplesFinder

Объект этого класса занимается поиском кратных чисел для списка чисел (ListOfNumbers). Внутри этого класса хранится информация о том каким числам должны быть кратны числа в списке $listNumbers. Метод isDuplicated проверяет не существует ли в число уже в списке кратных чисел. Метод isDivisible проверяет кратно ли число другому числу.

class MultiplesFinder
{
  private $multiples = [3, 5];
  /** @var ListOfNumbers */
  private $listNumbers;
  public function __construct(ListOfNumbers $listNumbers)
  {
    $this->listNumbers = $listNumbers;
  }
  public function find(): ListOfNumbers
  {
    $listOfMultiples = new ListOfNumbers();
    foreach ($this->listNumbers->iterator() as $number) {
      foreach ($this->multiples as $multiple) {
        if (
            $this->isDivisible($number, $multiple) &&
            !$this->isDuplicated($number, $listOfMultiples)
        ) {
           $listOfMultiples->add($number);
        }
      }
    }
    return $listOfMultiples;
  }
  private function isDuplicated(Number $number, ListOfNumbers $listOfNumbers)
  {
    foreach ($listOfNumbers->iterator() as $possibleDouble) {
      if ($possibleDouble->value === $number->value) {
        return true;
      }
    }
    return false;
  }
  private function isDivisible(Number $number, int $toNumber): bool
  {
    return $number->value % $toNumber === 0;
  }
}

Полный код программы можно скачать по ссылке на github. https://github.com/kosuha606/exercises/blob/master/Multiplesof3and5.php

Ссылки

  1. Задачи, из которых взята эта задача https://projecteuler.net/problem=1
  2. PHP Iterable https://wiki.php.net/rfc/iterable

Комментарии


Чтобы оставить комментарий зарегистрируйтесь.

Похожие статьи