1

Тема: РАМ(рівнодоступна адресна машина)

Потрібно правила відкорегувати. Сижу цілий день, напевно око замилилось проблемою. Прошу допомогти
Код взятий з наукового посібника іноземного

import math


class SimpleComputer:
    def __init__(self):
        self.registers = [0] * 20  # Список регістрів розміром 20, ініціалізованих нулями
        self.memory = []  # Пам'ять, список інструкцій, початково пустий
        self.pc = 0  # Лічильник програмний (Program Counter), початково 0
        self.halted = False  # Прапорець, що показує, чи припинено виконання програми

    def execute(self, program):
        self.memory = program[:]  # Копіюємо програму в пам'ять
        self.pc = 0  # Починаємо з початку програми
        self.halted = False  # Знімаємо прапорець зупинки

        while not self.halted:
            instruction = self.memory[self.pc]  # Беремо наступну інструкцію
            op, *args = instruction  # Розпаковуємо інструкцію, розділяючи опкод та аргументи

            if op == 'READ':  # Якщо опкод READ
                self.registers[args[0]] = int(input())  # Записуємо вказане користувачем значення в регістр
                self.pc += 1  # Переходимо до наступної інструкції

            elif op == 'WRITE':  # Якщо опкод WRITE
                if str(args[0]).startswith('='):  # Якщо аргумент починається з "=", то виводимо цифру
                    print(int(args[0][-1]))
                else:  # Інакше виводимо значення регістра
                    print(self.registers[args[0]])
                self.pc += 1  # Переходимо до наступної інструкції

            # Завантаження значення з пам'яті в регістр
            elif op == 'LOAD':
                if str(args[0]).startswith('='):
                    # Якщо аргумент починається з '=', то завантажуємо число, яке йде після '='
                    self.registers[0] = int(args[0][-1])
                else:
                    # Якщо ні, то завантажуємо значення з пам'яті в регістр
                    self.registers[0] = self.registers[args[0]]
                self.pc += 1

            # Збереження значення з регістру в пам'ять
            elif op == 'STORE':
                self.registers[args[0]] = self.registers[0]
                self.pc += 1

            elif op == 'MULT':
                # Множення значення з регістру на значення з пам'яті та збереження результату в регістр
                self.registers[0] *= self.registers[args[0]]
                self.pc += 1

            elif op == 'ADD':
                # Додавання значення з регістру до значення з пам'яті та збереження результату в регістр
                if str(args[0]).startswith('='):
                    # Якщо аргумент починається з '=', то додаємо число, яке йде після '='
                    self.registers[0] += int(args[0][-1])
                else:
                    # Якщо ні, то додаємо значення з пам'яті до значення в регістрі
                    self.registers[0] += self.registers[args[0]]
                self.pc += 1

            elif op == 'SUB':
                # Віднімання значення з пам'яті від значення в регістрі та збереження результату в регістр
                if str(args[0]).startswith('='):
                    # Якщо аргумент починається з '=', то віднімаємо число, яке йде після '='
                    self.registers[0] -= int(args[0][-1])
                else:
                    # Якщо ні, то віднімаємо значення з пам'яті від значення в регістрі
                    self.registers[0] -= self.registers[args[0]]
                self.pc += 1

            elif op == 'JUMP':
                # Перехід до певної комірки пам'яті
                next_index = self.memory.index((args[0],))
                if (next_index + 1) == len(self.memory):
                    # Якщо це остання комірка пам'яті, то pc необхідно встановити на поточну комірку
                    self.pc = next_index
                else:
                    # Інакше на наступну комірку перехід
                    self.pc = next_index + 1

            elif op == 'SWAP':
                # Розділяємо аргументи та сортуємо регістри між ними
                args = args[0].split()
                print(sorted(self.registers[int(args[0]):int(args[1])]))
                self.registers = self.registers[:int(args[0])] + sorted(
                    self.registers[int(args[0]):int(args[1])]) + self.registers[int(args[1]):]
                print(self.registers)
                # Збільшуємо лічильник команд на 1
                self.pc += 1

            elif op == 'COMPARE':
                # Обчислюємо квадратний корінь з вказаного регістру та віднімаємо його з регістру 0
                square = math.sqrt(self.registers[args[0]])
                self.registers[0] -= square
                # Збільшуємо лічильник команд на 1
                self.pc += 1

            elif op == 'JGTZ':
                # Перевіряємо, чи значення регістру 0 більше за 0
                if self.registers[0] > 0:
                    # Якщо так, то змінюємо значення лічильника команд на адресу вказану в аргументі та додаємо 1
                    self.pc = self.memory.index((args[0],)) + 1
                else:
                    # Якщо ні, то просто збільшуємо лічильник команд на 1
                    self.pc += 1

            elif op == 'JLTZ':
                # Перевіряємо, чи значення регістру 0 менше за 0
                if self.registers[0] < 0:
                    # Якщо так, то змінюємо значення лічильника команд на адресу вказану в аргументі та додаємо 1
                    self.pc = self.memory.index((args[0],)) + 1
                else:
                    # Якщо ні, то просто збільшуємо лічильник команд на 1
                    self.pc += 1

            # перевірка чи регістр 0 містить 0, якщо так - перехід на вказану адресу, інакше продовження виконання програми
            elif op == 'JZERO':
                if self.registers[0] == 0:
                    self.pc = self.memory.index((args[0],)) + 1
                else:
                    self.pc += 1

            #  перевіряє, чи значення в регістрі 0 не дорівнює першому аргументу операції JNE.
            elif op == 'JNE':
                args = args[0].split()
                if self.registers[0] != int(args[0]):
                    self.pc = self.memory.index((args[1],)) + 1
                else:
                    self.pc += 1

            # зупинка виконання програми
            elif op == 'HALT':
                break

            # якщо операція невідома, просто переходимо до наступної інструкції
            else:
                self.pc += 1

Ось реалізація парсингу правил

from ram import SimpleComputer

# Написати програму для РАМ, яка читає n додатніх чисел, обмежених 0 і друкує їх у порядку неспадання.
program1 = [
    ('WHILE',),
    ('LOAD', 1),
    ('JLTZ', 'ENDWHILE'),
    ('LOAD', 1),
    ('SUB', '=1'),
    ('STORE', 1),
    ('READ', 0),
    ('STORE', 2),
    ('READ', 0),
    ('STORE', 3),
    ('READ', 0),
    ('STORE', 4),
    ('READ', 0),
    ('STORE', 5),
    ('READ', 0),
    ('STORE', 6),
    ('READ', 0),
    ('STORE', 7),
    ('JUMP', 'ENDWHILE'),
    ('JUMP', 'WHILE'),
    ('ENDWHILE',),
    ('SWAP', '2 7'),
    ('WRITE', 2),
    ('WRITE', 3),
    ('WRITE', 4),
    ('WRITE', 5),
    ('WRITE', 6),
    ('WRITE', 7),
    ('HALT',)]

# Написати програму для РАМ, яка допускає всі входи виду 1^n 2^n^n 0.
program2 = [
    ('LOAD', '=0'),
    ('STORE', 1),
    ('LOAD', '=0'),
    ('STORE', 2),
    ('LOAD', '=0'),
    ('STORE', 3),
    ('WHILE1',),
    ('READ', 0),
    ('JNE', '1 WHILE2'),
    ('LOAD', 1),
    ('ADD', '=1'),
    ('STORE', 1),
    ('JUMP', 'WHILE1'),
    ('WHILE2',),
    ('JNE', '2 WHILE3'),
    ('LOAD', 2),
    ('ADD', '=1'),
    ('STORE', 2),
    ('WHILE2CONTINUE',),
    ('READ', 0),
    ('JNE', '2 WHILE3'),
    ('LOAD', 2),
    ('ADD', '=1'),
    ('STORE', 2),
    ('JUMP', 'WHILE2CONTINUE'),
    ('WHILE3',),
    ('JNE', '0 FAIL'),
    ('LOAD', 3),
    ('ADD', '=1'),
    ('JNE', '1 FAIL'),
    ('LOAD', 1),
    ('COMPARE', 2),
    ('JZERO', 'SUCCESS'),
    ('FAIL',),
    ('WRITE', '=0'),
    ('JUMP', 'HALT'),
    ('SUCCESS',),
    ('WRITE', '=1'),
    ('HALT',)
]

# Написати програму для РАМ, яка обчислює n^n з часовою оцінкою О(log2n) при рівномірному ваговому критерії.
program3 = [
    ('READ', 0),
    ('LOAD', 0),
    ('JGTZ', 'POS'),
    ('WRITE', 0),
    ('JUMP', 'ENDIF'),
    ('POS',),
    ('LOAD', 0),
    ('STORE', 1),
    ('STORE', 3),
    ('LOAD', 0),
    ('SUB', '=1'),
    ('STORE', 2),
    ('WHILE',),
    ('LOAD', 2),
    ('JGTZ', 'CONTINUE'),
    ('JUMP', 'ENDWHILE'),
    ('CONTINUE',),
    ('LOAD', 1),
    ('MULT', 3),
    ('STORE', 1),
    ('LOAD', 2),
    ('SUB', '=1'),
    ('STORE', 2),
    ('JUMP', 'WHILE'),
    ('ENDWHILE',),
    ('WRITE', 1),
    ('ENDIF',),
    ('HALT',)
]

# Написати програму для РАМ із використанням непрямої адресації, яка вводить масив.
program4 = [
    ('WHILE',),
    ('LOAD', 1),
    ('JLTZ', 'ENDWHILE'),
    ('LOAD', 1),
    ('SUB', '=1'),
    ('STORE', 1),
    ('READ', 0),
    ('STORE', 2),
    ('READ', 0),
    ('STORE', 2 + 1),
    ('READ', 0),
    ('STORE', 2 + 2),
    ('READ', 0),
    ('STORE', 2 + 3),
    ('READ', 0),
    ('STORE', 2 + 4),
    ('READ', 0),
    ('STORE', 2 + 5),
    ('JUMP', 'ENDWHILE'),
    ('JUMP', 'WHILE'),
    ('ENDWHILE',),
    ('WRITE', 2),
    ('WRITE', 2 + 1),
    ('WRITE', 2 + 2),
    ('WRITE', 2 + 3),
    ('WRITE', 2 + 4),
    ('WRITE', 2 + 5),
    ('HALT',)
]

# Написати програму на РАМ, яка підраховує кількість від’ємних чисел на вході (подано n чисел)
program5 = [
    ('LOAD', '=0'),
    ('STORE', 1),
    ('READ', 0),
    ('SUB', '=1'),
    ('STORE', 2),
    ('WHILE',),
    ('LOAD', 2),
    ('JLTZ', 'ENDWHILE'),
    ('LOAD', 2),
    ('SUB', '=1'),
    ('STORE', 2),
    ('READ', 0),
    ('JGTZ', 'WHILE'),
    ('JZERO', 'WHILE'),
    ('LOAD', 1),
    ('ADD', '=1'),
    ('STORE', 1),
    ('JUMP', 'WHILE'),
    ('ENDWHILE',),
    ('WRITE', 1),
    ('HALT',)
]

RAM = SimpleComputer()
print("Task 1")
RAM.execute(program1)
print("Task 2")
RAM.execute(program2)
print("Task 3")
RAM.execute(program3)
print("Task 4")
RAM.execute(program4)
print("Task 5")
RAM.execute(program5)

Відповідно до умов завдання працює не дуже вдало! Help please
WHILE і тд це просто умовні мітки для кращої читабельності

2

Re: РАМ(рівнодоступна адресна машина)

Georgeeee написав:

не дуже вдало

На жаль, через гарну погоду всі ясновидці цього форуму взяли відпустку за свій рахунок. Перепрошуємо за тимчасові труднощі, однак доведеться вам описати детальніше, що саме не дуже вдало.

3

Re: РАМ(рівнодоступна адресна машина)

в 1 завданні можна написати лише 6 чисел, немає перевірки на 0
в 2 заданні взагалі не зрозуміла умова( 1, бо 1 в будь якому 1, 16,бо 2 в 4 =16, 0, наче так)
в 3 завданні чи точно така часова складність
в 4 та 5 завданнях наче все добре

4

Re: РАМ(рівнодоступна адресна машина)

ґпт4 написав:

У першому завданні програма справді не перевіряє на 0. Якщо ви хочете додати таку перевірку, ви могли б додати ще одну операцію 'JZERO' перед 'READ' операціями, щоб перейти на 'HALT' у випадку нульового вводу.

У другому завданні ви правильно зрозуміли умову, але її трохи складно виразити через RAM програму. Програма має перевіряти, чи є кожне число, що рівне 1, вхідним числом, а потім перевіряти, чи є кожне число, що рівне 2, числом, що вводиться в степені кількості чисел 1, та зупинятись, коли зустрічається 0. Це складне завдання для RAM моделі, і воно може вимагати більшого програмування.

У третьому завданні часова складність виглядає правильною. Операція 'MULT' в RAM моделі виконується за O(1), і ви повторюєте цю операцію n разів, де n - це введене число. Таким чином, загальна часова складність програми є O(n). Якщо ви хочете зменшити часову складність до O(log2n), вам, можливо, знадобиться використовувати більш складний алгоритм, наприклад, алгоритм бінарного піднесення до степеня.

Згадайте, що RAM модель є досить простою моделлю обчислення, і не всі важкі обчислювальні задачі можуть бути легко виражені на ній. Для більш складних задач вам може знадобитися використовувати потужніші моделі обчислень, наприклад, Turing машини або реальні мови програмування.

5 Востаннє редагувалося koala (25.05.2023 12:10:07)

Re: РАМ(рівнодоступна адресна машина)

Ну тобто вам треба винайти асемблер.
Значить так. Пишете програму мовою високого рівня/псевдокодом, але з достатньою деталізацією (тобто замість функції sorted має бути в коді описаний алгоритм сортування). Записуєте в коментарях (чи на аркуші) розподіл змінних по регістрах, на кшталт
x -> reg[1]
y -> reg[2]
a -> reg[3]
b -> reg[4:20] #масив
А тоді переписуєте це все в код РАМ. Фрагменти псевдокоду лишаєте коментарями, щоб бачити, що і як.
Також я б порадив вам написати парсер, який би вмів читати текстовий файл із кодом, замість оцих тьюплів. Ну тобто реально імітувати асемблер.

І непряма адресація - це не
('STORE', 2 + 2), #Python обчислює 2+2=4
це коли адреса обчислюється в якомусь регістрі; ваша машина, схоже, такого не передбачає. Можлива семантика
STORE [N]
код
target = self.registers[int(args[0][1:-1])]
self.registers[target] = self.registers[0]

Що таке у вас ^ - я взагалі не уявляю.

Подякували: Georgeeee1