Равномерно распределить n элементов за m итераций

В контексте, это для управления несколькими шаговыми двигателями одновременно в высокоточном приложении.

Постановка задачи
Скажем, у меня есть цикл, который будет запускать итерации i. В ходе этих итераций экспрессия E_x должна оцениваться как truex раз (x <= i гарантирован).

Требования
- E_x должен оцениваться как trueточноx times
- E_x должен оцениваться до true через более или менее равномерно распределенные интервалы *

* «равномерно распределенные интервалы» означают, что максимальный размер интервала сведен к минимуму

Примеры
Для: i = 10, x = 7
E_x будет истинным на итерациях с пометкой 1: 1101101101

Для: i = 10, x = 3
E_x будет истинным на итерациях с пометкой 1: 0010010010

Для: i = 10, x = 2
E_x будет истинным на итерациях с пометкой 1: 0001000100

Каков наилучший (или даже «хороший») способ получить оценку E_x до true через равные промежутки времени, гарантируя при этом, что это верно в точности x раз?

Этот вопрос близок к моему, однако он предполагает, что E_x всегда будет оценивать true в 1-й и последней итерациях, что не соответствует моим требованиям (см. 2-й пример выше).

пожалуйста, объясните свой ответ для i = 10, x = 4, почему бы не 1001001001

Jasen 06.01.2019 23:47

@Jasen, это хороший аргумент, я не выбрал лучший пример. Смысл в том, чтобы показать, что может быть нежелательно иметь 1 в первой и / или последней позиции. Я дополню свой вопрос лучшими примерами.

Kyle G. 06.01.2019 23:58

Кайл, я прав в том, что ваш процесс - это всего лишь один запуск, а не бесконечный цикл (периодический процесс)? В противном случае ваш пример i = 10, x = 2 выглядит странно: разрыв между последним событием в одном цикле и первым в следующем составляет 5 0.

SergGr 07.01.2019 00:08

@SergGr вы правы. Это будет расширено для одновременного вращения нескольких шаговых двигателей, но с разными скоростями в течение заданного периода времени (в данном случае i - это временной интервал, а x - количество шагов, которые конкретный двигатель должен повернуть за этот временной интервал).

Kyle G. 07.01.2019 00:10

Напоминает мне этот вопрос. x здесь соответствует количеству символов там.

AShelly 07.01.2019 06:13
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
6
76
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Я буду использовать немного другое соглашение об именах: давайте по интервалам T запускать события [1..T] и N. Также давайте решим задачу как циклическую. Чтобы сделать это, давайте добавим один ложный шаг в конце, на котором мы гарантированно сработаем событие (и это также будет событие в момент времени 0, то есть перед циклом). Итак, мой T - это ваш i+1, а мой N - ваш x+1.

Если разделить T на N с напоминанием, получится T = w*N + r. Если r=0 дело тривиальное. Если r != 0, лучшее, что вы можете достичь, - это интервалы r размера w+1 и интервалы (N-r) размера w. Быстрое и простое, но достаточно хорошее решение будет примерно таким (псевдокод):

events = []
w = T / N
r = T % N
current = 0
for(i = 1; i<=N; i++) {
   current += w;
   if (i <= r)
      current += 1;
   events[i] = current;
}

Вы можете видеть, что последним значением в массиве будет T, как и было обещано нашим переформулированием как циклической проблемой. Это будет T, потому что в течение цикла мы добавим w к current, N раз и добавим r, умноженное на 1, так что сумма будет w*N+r, то есть T.

Главный недостаток этого решения состоит в том, что все «длинные» интервалы будут в начале, а все «короткие» интервалы будут в конце.

Вы можете распределять интервалы более равномерно, если будете немного умнее. И результирующая логика будет по существу такой же, как и за Линейный алгоритм Брезенхема, упомянутым в комментариях. Представьте, что вы рисуете линию на плоскости, где ось X представляет время, а ось Y представляет события, от (0,0) (которое является событием 0 до вашего временного интервала) до (i+1, x+1) (которое является событием x+1, просто по истечении вашего срока). Момент для создания события - это когда вы переключаетесь на следующий Y, то есть рисуете первый пиксель в данном Y.

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

Kyle G. 08.01.2019 02:51

@KyleG., Честно говоря, я не думаю, что любой из ответов идеален. Я не предоставляю явного кода для «лучшего» решения, и я считаю, что код Мэтта неверен в нециклических деталях вашей проблемы (рассмотрим случай x = 2 и n = 8, где решение, очевидно, должно быть 00100100, но я считаю, что код Мэтта выдает 01000100). Также разница между нашими ответами заключается в том, что я пытаюсь создать массив, в то время как код Мэтта представляет собой что-то вроде итератора / генератора, и неясно, что лучше для вашего варианта использования. Дайте мне знать, если вы хотите, чтобы я улучшил свой ответ более подробной информацией.

SergGr 08.01.2019 03:16

@KyleG., Спасибо, что приняли мой ответ. Вы не ответили на мой последний комментарий, но вы все равно можете сообщить мне здесь, если вам понадобится дополнительная помощь.

SergGr 15.01.2019 21:34

Если вы хотите делать приращения x по сравнению с итерациями n, вы можете сделать это следующим образом:

int incCount = 0;
int iterCount = 0;

boolean step() {
    ++iterCount;
    int nextCount = (iterCount*x + n/2) / n; // this is rounding division
    if (nextCount > incCount) {
        ++incCount;
        return true;
    }
    else {
        return false;
    }
}

Это простой для понимания способ. Если у вас встроенный ЦП, где разделение дороже, вы можете выполнить точно то же самое, например:

int accum = n/2;

boolean step() {
    accum+=x;
    if (accum >= n) {
        accum-=n;
        return true;
    }
    else {
        return false;
    }
}

Общая сумма, добавленная к accum, здесь, как и в первом примере, равна iterCount*x + n/2, но деление заменено инкрементным повторным вычитанием. Так работает алгоритм рисования линий Брезенхема.

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

Kyle G. 08.01.2019 02:51

Другие вопросы по теме