Umgekehrte Polnische Notation (Felder)
Damit zusammengesetzte Ausdrücke auch ohne Klammern geschrieben werden können, hat der polnische Mathematiker Jan Lukasiewicz eine Notation entworfen, welche die Operatoren neben den Zahlen und Variablen (und nicht dazwischen) aufführt.
Die hier verwendete Notation schreibt die Operatoren nach den Zahlen und wird "umgekehrte Polnische Notation" genannt.
Anstelle von "3 + 4" schreibt man hier "3 4 +". Damit können auch zusammengesetzte Ausdrücke ohne Klammern geschrieben werden. Anstelle von "(3 + 4) * (6 - 2)" schreibt man nun "3 4 + 6 2 - *". Es wird also zuerst 3 + 4 gerechnet, danach wird 6 - 2 bestimmt und am Schluss werden die beiden Faktoren miteinander multipliziert.
Achtung: Eine Änderung in der Reihenfolge der Operatoren hat natürlich auch eine Auswirkung auf die Auswertungsreihenfolge und somit auf das Resultat. Somit wird "3 4 + 6 - 2 *" wie folgt ausgewertet: "((3 + 4) - 6) * 2" .
Am einfachsten stellt man sich vor, die Zahlen werden der Reihe nach (von links nach rechts) auf Zettel geschrieben und aufeinadergetürmt (Stapel). Kommt nun ein Operator, so werden die beiden obersten Zettel vom Stapel genommen, miteinander verrechtet und am Schluss das Resultat wieder auf dem Stapel (auf einem neuen Zettel) aufgetürmt. (Die beiden verrechneten Zahlen werden nicht mehr auf den Stapel gelegt.)
Schreiben Sie nun ein Programm, welches beliebige Ausdrücke in umgekehrt polnischer Notation berechnen kann. Der Einfachheit halber verwenden wir nur ganze Zahlen und als Operatoren nur die vier Grundrechenarten (+, -, *, /).
Zusatzaufgabe (Schwierig): Schreiben Sie auch ein Programm, das die infix-Notation (A + B * C + D) in die postfix-Notation (A B C * + D +) umwandelt. Tipp: Die Lösung könnte iterativ (z. B. shunting-Yard Algorithmus) oder rekursiv geschehen.
3 Kommentare
6 Lösung(en)
package ch.programmieraufgaben.arrays.rpn;
import java.util.Arrays;
/**
* @author Philipp Gressly (phi AT gressly DOT ch)
*/
public class RPN {
int rpn(String ausdruck[]) {
int[] stack = new int[ausdruck.length / 2 + 1];
int top = -1;
int readPos = 0;
int resultat;
while(readPos < ausdruck.length) {
String symbol = ausdruck[readPos];
if("+".equals(symbol)) {
resultat = stack[top - 1] + stack[top];
top = top - 1;
stack[top] = resultat;
} else
if("*".equals(symbol)) {
resultat = stack[top - 1] * stack[top];
top = top - 1;
stack[top] = resultat;
} else
if("-".equals(symbol)) {
resultat = stack[top - 1] - stack[top];
top = top - 1;
stack[top] = resultat;
} else
if("/".equals(symbol)) {
resultat = stack[top - 1] / stack[top];
top = top - 1;
stack[top] = resultat;
}
else { // Kein Opreator
top = top + 1;
stack[top] = Integer.parseInt(symbol);
}
readPos = readPos + 1;
}
return stack[0];
}
public static void main(String[] args) {
new RPN().tests();
}
void tests() {
test(13, new String[] {"11", "2", "+"});
test(22, new String[] {"11", "2", "*"});
test(77, new String[] {"3", "4", "+", "11", "*"});
test( 2, new String[] {"22", "13", "2", "-", "/"});
}
void test(int erwartet, String[] ausdruck) {
int ist = rpn(ausdruck);
System.out.println(Arrays.toString(ausdruck) + " liefert " + ist + " ...");
if(ist == erwartet) {
System.out.println(" ... ok.");
} else {
System.out.println(" ... FEHLER!");
}
}
} // end of class RPN
Lösung von: Philipp G. Freimann (BBW (Berufsbildungsschule Winterthur) https://www.bbw.ch)
(defun calculate-upn-expression ()
"Berechnet ein UPN-Term. Die Eingabe erfolgt im Lisp-Stil,
also mit Klammern und Leerzeichen. Der Term '1', '2', '+'
wird also so eingegeben: (1 2 +) ."
(upn (read)))
(defun upn (expr)
(let ((stack nil))
(dolist (e expr)
(if (numberp e)
(push e stack)
(let ((right (pop stack)) (left (pop stack)))
(push (funcall e left right) stack))))
(car stack)))
Lösung von: Stefan Meichtry (Erwachsenenbildung)
// Autor: Andy Großhennig
// Solution for task: Umgekehrte Polnische Notation (Felder)
#include <iostream>
#include <deque>
#include <string>
using namespace std;
// Function: Get the math task
void getTask(string *sTask)
{
printf("Matheaufgabe eingeben. Zahlen zusammenschreiben und einzelne Elemente mit einem Leerzeichen trennen. (X XX + XXX -)\n");
getline(cin, *sTask);
}
// Function: Calculate the result
void calculate()
{
string sTask; //Math task
string::iterator s_iTask; //Iterator for math task
string sNumber; //Current number from the task
int iNumber = 0; //Between earning
deque<int>d_iNumbers; //Calculating stack
getTask(&sTask);
s_iTask = sTask.begin();
// Loop: Until the program reached the end of the math task
while(s_iTask != sTask.end())
{
sNumber = "";
// Switch: Check for number or operator
switch(*s_iTask)
{
case '+': iNumber = d_iNumbers.at(0) + d_iNumbers.at(1);
d_iNumbers.pop_front(); d_iNumbers.pop_front();
d_iNumbers.push_front(iNumber);
iNumber = 0;
s_iTask++;
break;
case '-': iNumber = d_iNumbers.at(1) - d_iNumbers.at(0);
d_iNumbers.pop_front(); d_iNumbers.pop_front();
d_iNumbers.push_front(iNumber);
iNumber = 0;
s_iTask++;
break;
case '*': iNumber = d_iNumbers.at(0) * d_iNumbers.at(1);
d_iNumbers.pop_front(); d_iNumbers.pop_front();
d_iNumbers.push_front(iNumber);
iNumber = 0;
s_iTask++;
break;
case '/': if(d_iNumbers.at(0) == 0)
{
printf("\nDivision durch 0 nicht moeglich");
return;
}
iNumber = d_iNumbers.at(1) / d_iNumbers.at(0);
d_iNumbers.pop_front(); d_iNumbers.pop_front();
d_iNumbers.push_front(iNumber);
iNumber = 0;
s_iTask++;
break;
case ' ': s_iTask++;
break;
default: if(*(s_iTask + 1) == ' ')
{
sNumber = *s_iTask;
}
else
{
while(*s_iTask != ' ')
{
sNumber += *s_iTask;
s_iTask++;
}
}
d_iNumbers.push_front(atoi(sNumber.c_str()));
s_iTask++;
break;
}
}
printf("\n%i", d_iNumbers.at(0)); //Result
}
int main()
{
calculate();
printf("\n\n");
system("Pause");
return 0;
}
Lösung von: Andy Großhennig (Bundeswehr)
function calculateRpn(str) {
str = str.split(" ");
var stack = [],
left, right;
for (var i = 0; i < str.length; i++) {
if (!isNaN(parseInt(str[i]))) stack.push(str[i]); // zahl
else { // operator
right = stack.pop(); left = stack.pop();
stack.push(eval(left + str[i] + right));
}
}
return stack.pop();
}
console.log(calculateRpn(prompt("Eingabe:")));
Lösung von: Lisa Salander (Heidi-Klum-Gymnasium Bottrop)
// NET 6.x | C# 10.x | VS-2022
var mathProblems = new List<string> { "3 4 + 6 2 - *", "22 13 2 - /", "11 2 +", "11 2 *", "3 4 + 11 *" };
mathProblems.ForEach(x => Console.WriteLine($"{x} = {PostfixNotation(x)}"));
double PostfixNotation(string mathProblem) {
var split = mathProblem.Split(" ");
var stack = new Stack<int>();
for (var i = 0; i < split.Count(); i++) {
var isNumeric = int.TryParse(split[i], out var val);
if (isNumeric) stack.Push(val);
else {
int first = stack.Pop(), second = stack.Pop();
stack.Push(split[i] switch {
"+" => second + first,
"-" => second - first,
"*" => second * first,
"/" => second / first,
_ => 0
});
}
}
return stack.Pop();
}
Lösung von: Jens Kelm (@JKooP)
// C++ 17
#include <iostream>
#include <sstream>
#include <vector>
#include <stack>
#include <algorithm>
#include <vector>
using is_it = std::istream_iterator<std::string>;
double get_postfix_notation(std::string math_problem) {
const auto get_split_string{ [](const std::string& s) {std::istringstream iss(s); std::vector<std::string> r((is_it(iss)), is_it()); return r; } };
const auto is_number{ [](const std::string& s) {return !s.empty() && std::all_of(s.begin(), s.end(), ::isdigit); } };
std::stack<double> stack{};
const auto split{ get_split_string(math_problem) };
for (auto it{ split.begin() }; it != split.end(); ++it) {
if (is_number(*it))
stack.push(std::stoi(*it));
else {
const auto first{ stack.top() }; stack.pop();
const auto second{ stack.top() }; stack.pop();
if (*it == "+") stack.push(second + first);
else if (*it == "-") stack.push(second - first);
else if (*it == "*") stack.push(second * first);
else if (*it == "/") stack.push((double)second / first);
else stack.push(0);
}
}
return stack.top();
}
int main() {
std::vector<std::string>v{ "3 4 + 6 2 - *", "22 13 2 - /", "11 2 +", "11 2 *", "3 4 + 11 *" };
for (const auto& i : v)
std::cout << i << " = " << get_postfix_notation(i) << "\n";
}
Lösung von: Jens Kelm (@JKooP)
Verifikation/Checksumme:
"11", "2", "+" liefert 13
"11", "2", "*" liefert 22
"3", "4", "+", "11", "*" liefert 77
"22", "13", "2", "-", "/" liefert 2
Aktionen
Neue Lösung hinzufügen
Bewertung
Durchschnittliche Bewertung:
Meta
Zeit: | 1 |
Schwierigkeit: | Schwer |
Webcode: | vwsv-wf6p |
Autor: | Philipp G. Freimann (BBW (Berufsbildungsschule Winterthur) https://www.bbw.ch) |
Kommentare (3)
Ja, als Zusatzaufgabe passt es.
Ich habe mal meine Python-Lösung abgeändert.
Ja, sicher klar. Ist aber eine neue Aufgabe. Wir mussten dies als Übung an der Uni lösen. Vielleicht passt es auch als Zusatzaufgabe?
=> Shunting-yard-Algorithmus