Привет ! Меня зовут Ксавье Жувено, и это шестая часть длинной серии статей Вызов современного C++. В этой статье я собираюсь объяснить, как я решил шестую проблему на C++ и как я интегрировал это решение в проект Android.

Цель этой шестой задачи проста. Мы должны вывести все избыточные числа и их изобилие, вплоть до предела, введенного пользователем. Решение будет вычислено на C++, а интерфейс для получения пользовательского ввода и отображения результата будет обрабатываться с помощью Android Studio Framework.

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

Что такое обильное число?

› Обильное число — это число, у которого сумма его собственных делителей больше, чем само число. Величина, на которую сумма превышает число, является изобилием.

Например, число 12 можно разделить на 1, 2, 3, 4, 6 и само на себя. Таким образом, его правильными делителями являются 1, 2, 3, 4, 6, и если вы их суммируете, вы получите 1+2+3+4+6 = 16, что больше 12. Таким образом, 12 — это обильное число с обилием 16-12 = 4.

Решение на С++

Прежде всего, мы должны уметь решать задачу с помощью C++. Для этого мы реализуем 3 функции. 🙂

Первый позволяет нам получить сумму всех делителей числа.

static constexpr unsigned int getSumOfDivisors(unsigned int number) {
  unsigned int sumOfDivisors{0};
  for(auto i = 1; i <number; ++i) {
    if(number % i == 0) { sumOfDivisors += i; }
  }
  return sumOfDivisors;
}

Вторая функция позволяет нам узнать, является ли число обильным числом, и получить обилие, если это обильное число. Эта функция вернет std::Optional, который будет содержать значение обилия, или std::nullopt, если ввод не является обильным числом.

std::optional getAbundance(unsigned int number) {
  const auto sum = getSumOfDivisors(number);
  if(number < sum) { return {static_cast(sum - number)}; }
  return std::nullopt;
}

Третья и последняя функция позволяет нам получить все обильные числа с их обилием до предела, заданного в параметрах.

std::vector&lt;std::pair&gt;
getAllAbundantNumbersUpTo(unsigned int upperLimit) {
  std::vector<std::pair> results;
  for(auto i = 1; i <= upperLimit; ++i) {
    auto potentialAbundantNumber = i;
    auto abundance = getAbundance(i);
    if(abundance.has_value()) {
results.emplace_back(std::make_pair(std::move(potentialAbundantNumber), std::move(*abundance)));
    }
  }
  return results;
}

С этими тремя функциями мы можем решить проблему без проблем 😉

Интерфейс пользовательского интерфейса в Android Studio

Пользовательский интерфейс для этой задачи аналогичен интерфейсу предыдущей задачи. Поэтому я не буду вдаваться в подробности, но я призываю вас заглянуть в мой пост об этом в блоге. 😉

Но даже если интерфейс для задачи похож на две предыдущие задачи, я хочу поговорить с вами о пользовательском интерфейсе, который мне пришлось внедрить. Действительно, теперь, когда у нас есть 6 задач, решенных в нашем приложении, необходимость много раз нажимать кнопку «Далее», чтобы добраться до последней задачи, занимает много времени и будет увеличиваться каждый раз, когда мы добавляем новое решение проблемы.

Чтобы избежать этого, мы создали пользовательский интерфейс, похожий на меню, где вы можете нажать на проблему, к которой хотите перейти. Для этого мы создали новый пользовательский интерфейс с 6 кнопками, открывающими один проблемный интерфейс.

Вот методы, которые я использовал:

void addButton(@IdRes int id, String text) {
  RelativeLayout ll = findViewById(R.id.buttons);
  RelativeLayout.LayoutParams newParams =
    new RelativeLayout.LayoutParams(
      RelativeLayout.LayoutParams.WRAP_CONTENT,
      RelativeLayout.LayoutParams.WRAP_CONTENT);
  Button b = new Button(this);
  b.setId(id);
  b.setText(text);
  b.setOnClickListener(this);
  newParams.addRule (RelativeLayout.ALIGN_LEFT);
  b.setLayoutParams(newParams);
  ll.addView(b);
}
void addButton(@IdRes int id, String text, @IdRes int idOfLeftElement) {
  RelativeLayout ll = findViewById(R.id.buttons);
  RelativeLayout.LayoutParams newParams =
    new RelativeLayout.LayoutParams(
      RelativeLayout.LayoutParams.WRAP_CONTENT,
      RelativeLayout.LayoutParams.WRAP_CONTENT);
  Button b = new Button(this);
  b.setId(id);
  b.setText(text);
  b.setOnClickListener(this);
  newParams.addRule (RelativeLayout.RIGHT_OF, idOfLeftElement);
  b.setLayoutParams(newParams);
  ll.addView(b);
}
void addButtonBelow(@IdRes int id, String text, @IdRes int idOfElementOnTop) {
  RelativeLayout ll = findViewById(R.id.buttons); 
  RelativeLayout.LayoutParams newParams =
    new RelativeLayout.LayoutParams(
      RelativeLayout.LayoutParams.WRAP_CONTENT,
      RelativeLayout.LayoutParams.WRAP_CONTENT);
  Button b = new Button(this);
  b.setId(id);
  b.setText(text);
  b.setOnClickListener(this);
  newParams.addRule (RelativeLayout.BELOW, idOfElementOnTop);
  b.setLayoutParams(newParams);
  ll.addView(b);
}
void addButtonBelow(@IdRes int id, String text, @IdRes int idOfLeftElement, @IdRes int idOfElementOnTop) {
  RelativeLayout ll = findViewById(R.id.buttons);
  RelativeLayout.LayoutParams newParams =
    new RelativeLayout.LayoutParams(
      RelativeLayout.LayoutParams.WRAP_CONTENT,
      RelativeLayout.LayoutParams.WRAP_CONTENT);
  Button b = new Button(this);
  b.setId(id);
  b.setText(text);
  b.setOnClickListener(this);
  newParams.addRule (RelativeLayout.RIGHT_OF, idOfLeftElement);
  newParams.addRule (RelativeLayout.BELOW, idOfElementOnTop);
  b.setLayoutParams(newParams);
  ll.addView(b);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  addButton(R.id.menu_button_1, "1");
  addButton(R.id.menu_button_2, "2", R.id.menu_button_1);
  addButton(R.id.menu_button_3, "3", R.id.menu_button_2);
  addButton(R.id.menu_button_4, "4", R.id.menu_button_3);
  addButtonBelow(R.id.menu_button_5, "5", R.id.menu_button_1);
  addButtonBelow(R.id.menu_button_6, "6", R.id.menu_button_5, R.id.menu_button_2);
}
public void onClick(View v) {
  switch (v.getId()) {
    case R.id.menu_button_1:
      startActivity(new Intent(this, Problem_1.class));
    break;
    case R.id.menu_button_2:
      startActivity(new Intent(this, Problem_2.class));
    break;
    case R.id.menu_button_3:
      startActivity(new Intent(this, Problem_3.class));
    break;
    case R.id.menu_button_4:
      startActivity(new Intent(this, Problem_4.class));
    break;
    case R.id.menu_button_5:
      startActivity(new Intent(this, Problem_5.class));
    break;
    case R.id.menu_button_6:
      startActivity(new Intent(this, Problem_6.class));
    break;
    default:
      new AssertionError("Unknown menu button.");
    break;
  }
}

Не волнуйтесь, я объясню этот код. 😉

Прежде всего, методы addButton и addButtonBelow создают кнопку в пользовательском интерфейсе с правильным текстом и в правильном положении, используя RelativeLayout, помещенный в пользовательский интерфейс в XML-определении пользовательского интерфейса.

Затем у нас есть метод onCreate, который вызывает ранее определенные методы для добавления кнопок с правильными параметрами.

И, наконец, метод onClick вызывается при нажатии кнопки и открывает нужную активность в зависимости от нажатия кнопки. Это означает, что класс реализует класс View.OnClickListener. 😉

Таким образом, мы получаем меню, позволяющее нам перейти непосредственно к проблеме, которую мы хотим.

Использование собственного кода C++

Связывание пользовательского интерфейса с кодом C++ очень похоже на предыдущую задачу.

extern "C" JNIEXPORT jstring JNICALL Java_com_example_themoderncppchallenge_Problem_16_AbundantNumbersUpTo(JNIEnv *env, jobject thiz, jint user_input) {
  const auto& result = getAllAbundantNumbersUpTo(user_input);
  std::string text;
  for(const auto& abundance : result) {
    text += '(' + std::to_string(abundance.first) + ", " + std::to_string(abundance.second) + "), ";
  }
  // Removes the last ", "
  text.pop_back();
  text.pop_back();
  return env->NewStringUTF(text.c_str());
}

Мы начинаем с получения результатов от ранее определенной функции и преобразуем эти данные в string перед отправкой вызывающей стороне.

Вывод

Вуаля! Теперь у нас есть приложение, которое может решить первые шесть задач The Modern C++ Challenge.

Вы можете заметить, что решения, написанные в этом посте, включают не все исходники для создания работающей программы, а только интересную часть исходников для решения этой проблемы. Если вы хотите увидеть программы от начала до конца, вы можете зайти на мою учетную запись GitHub, изучить полное решение, добавить комментарии или задать вопросы, если хотите, на платформе, на которой вы читаете эту статью, она также поможет мне. улучшить качество моих статей.

Спасибо всем за прочтение этой статьи, и до моей следующей статьи, хорошего дня 😉

Интересные ссылки

Первоначально опубликовано на http://10xlearner.com 27 апреля 2020 г.