У меня есть генератор, который производит все положительные целые числа, которые являются полномочиями 2, и другой, который производит все целые числа, которые являются полномочиями 3. Я теперь должен использовать их для создания целых чисел формы 2^i*3^j где я, j> =0,0 в увеличивающемся порядке.
Точка использования генераторов должна уменьшить потребление памяти, я думаю. Я пытался сделать это некоторое время теперь напрасно. Выручите.
Вы можете решить эту проблему, используя поток самочтения:
----------- -----------
| pow 2 |------->| |
----------- | |
| merge |-------+------------>
----------- | | |
.->| x 3 |------->| | |
| ----------- ----------- |
\_______________________________________/
Первый поток производит силу двойки, в то время как второй гарантирует, что все сгенерированные числа умножаются на 3 и повторно вводятся в выходной сигнал. Оператор объединения обеспечивает сортировку выхода.
Обратите внимание, что мы должны "посадить" выходной поток 1, Или первый элемент будет пытаться производить себя сам при вычислении.
Вот код:
(require srfi/41)
(define (merge s1 s2)
(stream-match s1 ((x . xs)
(stream-match s2 ((y . ys)
(if (< x y)
(stream-cons x (merge xs s2))
(stream-cons y (merge ys s1))))))))
(define (the-stream)
(letrec ((s
(stream-cons 1 (merge (stream-map (lambda (x) (* 3 x)) s)
(stream-iterate (lambda (x) (* 2 x)) 2)))))
s))
Он довольно прост и быстр по сравнению с моим другим предложением,
потому что помимо монотонности она использует арифметические свойства проблемы.
Я ошибаюсь, ее можно обобщить так же хорошо (в скором времени)
$ mzscheme -f feedback.scm -e '(display (stream->list (stream-take 20 (the-stream))))'
(1 2 3 4 6 8 9 12 16 18 24 27 32 36 48 54 64 72 81 96)
$ time mzscheme -f feedback.scm -e '(display (stream-ref (the-stream) 10000))'
161968247347450370721577384417107686788864605658546176
real 0m1.746s
user 0m1.344s
sys 0m0.156s
Мы также можем реализовать это с помощью генераторов Питона, но нам нужно использовать очередь для хранения ожидающих номеров в цикле обратной связи:
# Merge the output of two generators
def merge(g1, g2):
v1 = g1.next()
v2 = g2.next()
while 1:
if v1 < v2:
yield v1
v1 = g1.next()
else:
yield v2
v2 = g2.next()
# Generates the powers of 2, starting with n
def pow2(n):
while 1: yield n; n *= 2
# Generates values shifted from the given 'q' and multiplied by 3
def mul3(q):
while 1: yield q.pop(0) * 3
# The generator we want
def pow23():
q = []
v = 1
g = merge(pow2(2), mul3(q))
while 1:
yield v
q.append(v)
v = g.next()
g23 = pow23()
for i in range(10000): g23.next()
print g23.next()
Это несколько менее элегантно (IMHO), но генераторы намного легче:
$ time python feedback.py
161968247347450370721577384417107686788864605658546176
real 0m0.150s
user 0m0.112s
sys 0m0.012s
Я сделал реализацию схемы. (используя затворы в качестве генераторов) который показывает примерно такую же производительность.
Это довольно просто в Хаскелле:
merge as bs =
case (as, bs) of
([], _) -> bs
(_, []) -> as
((a:as'), (b:bs')) ->
if a <= b
then a : (merge as' bs)
else b : (merge as bs')
rmDups as =
case as of
[] -> []
[a] -> [a]
(a:bs@(b:_)) ->
if a == b
then rmDups bs
else a:(rmDups bs)
take 25 $ rmDups $ merge (map (2^) [1..]) (map (3^) [1..])
дает следующее:
[2,3,4,8,9,16,27,32,64,81,128,243,256,512,729,1024,2048,2187,4096,6561,8192,16384,19683,32768,59049]
хотя я представляю, что есть более элегантный способ сделать это...
.] Просто объедините два заказанных списка []a la[] [
] [(define merge
(lambda (pred ls1 ls2)
(cond
[(null? ls1) ls2]
[(null? ls2) ls1]
[(pred (car ls1) (car ls2))
(cons (car ls1) (merge pred (cdr ls1) ls2))]
[else (cons (car ls2) (merge pred ls1 (cdr ls2)))])))
]
[], поднятых из [] здесь [].[
]] Простое решение для любого примера - это создание нового.[
] [for (i = 0; i < X; i++)
{
if (i%2 or i%3)
{
cout << i
}
}
]
[]edit: X - это сколько времени вы хотите запустить его, скажем, вы хотите вывести 0-100 put 100.[
] [int counter = 1000;
bool done = false;
while(!done)
{
if (i%2 or i%3)
{
cout << i;
counter--;
if(counter <= 1)
{
done = true;
}
}
i++;
}
]
[]Это немного запутанно, но должно работать.[
] []edit: Счетчик должен заканчиваться на 1, или он даст вам 1001 элемент.[
].]По крайней мере, если я понимаю ваш вопрос, вам нужно просто объединить результаты от двух генераторов:[
] []Если два генератора производят равные значения, генерировать их в качестве выхода, и генерировать следующее значение от каждого генератора. [
] []Обратите внимание, что хотя он обычно используется для сортировки существующих данных вместо генерации новых, это похоже на слияние, используемое в обычной сортировке слияния, за исключением того, что я предположил, что вам не нужны дубликаты, где в сортировке слияния обычно сохраняются дубликаты. [
] []Правка: Благодаря lpthnc я перечитал вопрос, и думаю, что он прав - я неправильно понял исходный вопрос. Чтобы получить правильный вывод, нужно создать третий генератор и произвести кратные числа (в данном случае) шести, а также использовать трехходовое слияние между этим результирующим множеством и двумя другими генераторами.[
] []Я не очень-то много с ним играл, но я считаю, что уровень языка Lazy (или ленивый модуль) в последних итерациях PLT Scheme позволил бы вам написать код для генерации всей бесконечной последовательности, которая теоретически использовала бы бесконечное время и память, но оценивала бы только конечное подмножество этого по мере необходимости.[
].]Redacted. Чем больше я смотрю на это, тем больше мне кажется, что я всё неправильно понял - и у других, похоже, уже есть ответы получше.[
] [][]
Извините, ничего этого нет в схеме, только псевдокод...[][
]Следующий код соответствует мыслительному процессу, который я получил из вашего вопроса:[
] []EDIT: переработал псевдокод, теперь, когда я понял, что это "2^i*3^j", а не "2^i, 3^j"[
] [// If i got close, this time, // inputs min-i=0, max-i=2, min-j=0, max-j=2 // should get output like // 2^0 * 3^0 = 1 // 2^0 * 3^1 = 3 // 2^0 * 3^2 = 6 // 2^1 * 3^0 = 2 // 2^1 * 3^1 = 6 // 2^1 * 3^2 = 12 // 2^2 * 3^0 = 4 // 2^2 * 3^1 = 12 // 2^2 * 3^2 = 24 LET min-i, max-i, min-j, max-j be input LET current-value = 1 FOR i = min-i to max-i FOR j = min-j to max-j DO PRINT "2^" . i . " * j^" . j . " = " . current-value current-value *= 3; DONE // end j loop current-value *= 2 DONE // end i loop] [
][
]Я не очень разбираюсь в генераторах, однако я могу предложить решение, основанное на потоках (лениво построенных, возможно бесконечные списки), которые в чем-то похожи.
Мой подход - создать поток чье «состояние» само было бы потоком потоков.
Индивидуальные внутренние потоки чисел, назовем их 3-потоками, будет представлять списки последовательных степеней 3, начиная с 1, умноженное на заданную степень двойки. Затем мы можем собрать бесконечное количество таких 3-потоков, по одному на каждую последовательную степень двойки, начиная с 1. Назовем это двухпотоковым.
Начальное состояние в ascii-art следующее:
---------------------- --- -- -
| The 2-stream ...
--|----|----|----|---- --- -- -
V V V V
|1| | 2| | 4| | 8|
|3| | 6| |12| |24| ...
|9| |18| |36| |72| The 3-streams
: : : :
Теперь мы собираемся манипулировать этим так, чтобы в любой момент 3 потока будут упорядочены внутри 2 потока Что касается их первых элементов. Как следствие, следующее наименьшее сгенерированное число всегда будет первым элементом первого 3-потока.
Итак, чтобы получить следующее число в последовательности, которую вы хотите получить, мы собираемся вытащить первые 3 потока, вытащите его первый элемент (это число, которое нас интересует), а затем повторно вставьте 3-поток в 2-поток в позиции, определяемой его новым первым элементом. Новое состояние после извлечения первого числа (1) будет следующим:
---------------------- --- -- -
| The 2-stream ...
---|----|----|----|---- --- -- -
V V V V
| 2| | 3| | 4| | 8|
| 6| | 9| |12| |24| ...
|18| |27| |36| |72| The 3-streams
: : : :
Обратите внимание, что этот метод не зависит конкретно от 2 ^ i, 3 ^ j или умножения.
(только на 2 ^ i * 3 ^ j, монотонно возрастающем с i и j).
Я опубликовал еще один ответ, и
в результате намного проще и быстрее .
не верьте мне: это не имеет ничего общего с математикой
Ниже приведен пример реализации с использованием потоков SRFI-41 :
(require srfi/41)
; Geometric sequence with initial value 'init', and ratio 'r'
(define (make-geoseq init r)
(stream-cons
init
(make-geoseq (* r init) r)))
; Your power generators
(define pow2 (make-geoseq 1 2))
(define pow3 (make-geoseq 1 3))
; Construct a 3-stream from the pow3 sequence
(define (make-3stream mult)
(stream-map (lambda (x) (* mult x)) pow3))
; Construct the (initial) 2-stream from the pow2 sequence
(define initial-2stream
(stream-map make-3stream pow2))
; Insert a modified 3-stream into the given 2-stream, at the right position
(define (insert two-stream three-stream)
(if (< (stream-car three-stream)
(stream-car (stream-car two-stream)))
; we have the smallest 3-stream, put it at the front
(stream-cons
three-stream
two-stream)
; otherwise, recurse
(stream-cons
(stream-car two-stream)
(insert (stream-cdr two-stream) three-stream))))
; Construct a 2^n * 3^p stream with the given 2-stream as its "state"
(define (make-the-stream current-2stream)
(let*
; pull out the first 3-stream
((first-3s (stream-car current-2stream))
(other-3s (stream-cdr current-2stream))
; use its first element as our next value
(next-val (stream-car first-3s))
; reinsert its tail into the 2-stream's tail
(next-2s (insert other-3s (stream-cdr first-3s))))
; and use the resulting 2-stream to construct the (outer) stream's tail
(stream-cons
next-val
(make-the-stream next-2s))))
; Now, we can construct the stream we want
(define the-stream (make-the-stream initial-2stream))
Использование схемы plt (на моем довольно дрянном аппаратное обеспечение):
$ mzscheme -f pow23.scm -e '(display (stream->list (stream-take 20 the-stream)))'
(1 2 3 4 6 8 9 12 16 18 24 27 32 36 48 54 64 72 81 96)
$ time mzscheme -f pow23.scm -e '(display (stream-ref the-stream 10000))'
161968247347450370721577384417107686788864605658546176
real 0m12.550s
user 0m11.005s
sys 0m0.340s
Я думаю, реализовать это с помощью генераторов можно,
но сложнее всего будет реализовать (вставить)
.
Вы можете сделать это, составив генераторы,
но вы в конечном итоге добавляете один "слой" каждый раз, когда вытаскивают число,
тогда как поток, созданный с помощью (вставить)
, разделяет свой хвост с исходным
(«слои» со временем сливаются).