Stellen Sie die theoretische Arithmetik ein
Prämisse
Es gab bereits einige Herausforderungen, bei denen ohne den Multiplikationsoperator ( hier und hier ) multipliziert wurde, und diese Herausforderung ist in der gleichen Richtung (am ähnlichsten wie beim zweiten Link).
Diese Herausforderung verwendet im Gegensatz zu den vorherigen eine festgelegte theoretische Definition der natürlichen Zahlen ( N ):
und
zum Beispiel,
und so weiter.
Die Herausforderung
Unser Ziel ist es, Mengenoperationen (siehe unten) zu verwenden, um natürliche Zahlen zu addieren und zu multiplizieren. Zu diesem Zweck befinden sich alle Einträge in derselben 'festgelegten Sprache', deren Interpreter unten aufgeführt ist . Dies sorgt für Konsistenz und erleichtert die Bewertung.
Mit diesem Interpreter können Sie natürliche Zahlen als Mengen bearbeiten. Ihre Aufgabe wird es sein, zwei Programmkörper (siehe unten) zu schreiben, von denen einer natürliche Zahlen hinzufügt, der andere sie multipliziert.
Vorbemerkungen zu Sets
Mengen folgen der üblichen mathematischen Struktur. Hier sind einige wichtige Punkte:
- Sets werden nicht bestellt.
- Kein Satz enthält sich selbst
- Elemente sind entweder in einer Menge oder nicht, dies ist boolesch. Daher können Mengenelemente keine Multiplizitäten haben (dh ein Element kann nicht mehrmals in einer Menge enthalten sein.)
Dolmetscher und Besonderheiten
Ein 'Programm' für diese Herausforderung ist in 'festgelegter Sprache' geschrieben und besteht aus zwei Teilen: einem Header und einem Body.
Header
Der Header ist sehr einfach. Es teilt dem Dolmetscher mit, welches Programm Sie lösen. Der Header ist die Eröffnungszeile des Programms. Es beginnt entweder mit dem Zeichen +
oder *
, gefolgt von zwei Ganzzahlen, die durch Leerzeichen getrennt sind. Zum Beispiel:
+ 3 5
oder
* 19 2
sind gültige Header. Der erste zeigt an, dass Sie versuchen zu lösen 3+5
, was bedeutet, dass Ihre Antwort sein sollte 8
. Die zweite ist ähnlich, außer bei der Multiplikation.
Körper
Im Körper befinden sich Ihre eigentlichen Anweisungen an den Dolmetscher. Dies ist wirklich das, was Ihr "Additions" - oder "Multiplikations" -Programm ausmacht. Ihre Antwort besteht aus zwei Programmkörpern, einem für jede Aufgabe. Anschließend ändern Sie die Header, um die Testfälle tatsächlich auszuführen.
Syntax und Anweisungen
Anweisungen bestehen aus einem Befehl, gefolgt von null oder mehr Parametern. Für die folgenden Demonstrationen ist jedes Alphabetzeichen der Name einer Variablen. Denken Sie daran, dass alle Variablen Mengen sind. label
ist der Name eines Labels (Labels sind Wörter, denen Semikolons folgen (dh main_loop:
), int
ist eine Ganzzahl. Das Folgende sind die gültigen Anweisungen:
jump label
springe bedingungslos zum Etikett. Ein Label ist ein 'Wort', gefolgt von einem Semikolon: zBmain_loop:
ist ein Label.je A label
Zum Etikett springen, wenn A leer istjne A label
Zur Beschriftung springen, wenn A nicht leer istjic A B label
Springe zum Etikett, wenn A B enthältjidc A B label
Springe zum Etikett, wenn A nicht B enthält
print A
Gibt den wahren Wert von A aus, wobei {} die leere Menge istprinti variable
Gibt eine ganzzahlige Darstellung von A aus, falls vorhanden, andernfalls wird ein Fehler ausgegeben.
;
Das Semikolon gibt an, dass der Rest der Zeile ein Kommentar ist und vom Interpreter ignoriert wird
Weitere Informationen
Zu Beginn des Programms gibt es drei bereits vorhandene Variablen. Sie sind set1
,set2
und ANSWER
. set1
Nimmt den Wert des ersten Header-Parameters. set2
nimmt den Wert der Sekunde. ANSWER
ist zunächst die leere Menge. Nach Abschluss des Programms prüft der Interpreter, ob ANSWER
es sich um die ganzzahlige Darstellung der Antwort auf das im Header definierte arithmetische Problem handelt. Wenn dies der Fall ist, wird dies mit einer Meldung an stdout angezeigt.
Der Interpreter zeigt auch die Anzahl der verwendeten Operationen an. Jede Anweisung ist eine Operation. Das Initiieren eines Etiketts kostet ebenfalls einen Vorgang (Etiketten können nur einmal initiiert werden).
Sie können maximal 20 Variablen (einschließlich der 3 vordefinierten Variablen) und 20 Beschriftungen haben.
Dolmetschercode
WICHTIGE HINWEISE ZU DIESEM INTERPRETERBei Verwendung großer Zahlen (> 30) in diesem Interpreter sind die Dinge sehr langsam. Ich werde die Gründe dafür skizzieren.
- Die Strukturen von Mengen sind so, dass Sie durch Erhöhen um eine natürliche Zahl die Größe der Mengenstruktur effektiv verdoppeln. Die n- te natürliche Zahl enthält 2 ^ n leere Mengen (damit meine ich, wenn Sie n als Baum betrachten, gibt es n leere Mengen. Beachten Sie, dass nur leere Mengen Blätter sein können.) Dies bedeutet, dass der Umgang mit 30 von Bedeutung ist teurer als der Umgang mit 20 oder 10 (Sie sehen 2 ^ 10 vs 2 ^ 20 vs 2 ^ 30).
- Gleichheitsprüfungen sind rekursiv. Da Sets angeblich ungeordnet sind, schien dies der natürliche Weg zu sein, dies anzugehen.
- Es gibt zwei Speicherlecks, die ich nicht beheben konnte. Ich bin schlecht in C / C ++, sorry. Da es sich nur um kleine Zahlen handelt und der zugewiesene Speicher am Programmende freigegeben wird, sollte dies eigentlich kein so großes Problem sein. (Bevor jemand etwas sagt, ja, ich weiß davon
std::vector
; ich habe dies als Lernübung gemacht. Wenn Sie wissen, wie man es behebt, lassen Sie es mich bitte wissen und ich werde die Änderungen vornehmen, andernfalls werde ich es verlassen, da es funktioniert wie es ist.)
Beachten Sie auch den Include-Pfad zu set.h
in der interpreter.cpp
Datei. Ohne weiteres der Quellcode (C ++):
set.h
using namespace std;
//MEMORY LEAK IN THE ADD_SELF METHOD
class set {
private:
long m_size;
set* m_elements;
bool m_initialized;
long m_value;
public:
set() {
m_size =0;
m_initialized = false;
m_value=0;
}
~set() {
if(m_initialized) {
//delete[] m_elements;
}
}
void init() {
if(!m_initialized) {
m_elements = new set[0];
m_initialized = true;
}
}
void uninit() {
if(m_initialized) {
//delete[] m_elements;
}
}
long size() {
return m_size;
}
set* elements() {
return m_elements;
}
bool is_empty() {
if(m_size ==0) {return true;}
else {return false;}
}
bool is_eq(set otherset) {
if( (*this).size() != otherset.size() ) {
return false;
}
else if ( (*this).size()==0 && otherset.size()==0 ) {
return true;
}
else {
for(int i=0;i<m_size;i++) {
bool matched = false;
for(int j=0;j<otherset.size();j++) {
matched = (*(m_elements+i)).is_eq( *(otherset.elements()+j) );
if( matched) {
break;
}
}
if(!matched) {
return false;
}
}
return true;
}
}
bool contains(set set1) {
for(int i=0;i<m_size;i++) {
if( (*(m_elements+i)).is_eq(set1) ) {
return true;
}
}
return false;
}
void add(set element) {
(*this).init();
bool alreadythere = false;
for(int i=0;i<m_size;i++) {
if( (*(m_elements+i)).is_eq(element) ) {
alreadythere=true;
}
}
if(!alreadythere) {
set *temp = new set[m_size+1];
for(int i=0; i<m_size; i++) {
*(temp+i)= *(m_elements+i);
}
*(temp+m_size)=element;
m_size++;
delete[] m_elements;
m_elements = new set[m_size];
for(int i=0;i<m_size;i++) {
*(m_elements+i) = *(temp+i);
}
delete[] temp;
}
}
void add_self() {
set temp_set;
for(int i=0;i<m_size;i++) {
temp_set.add( *(m_elements+i) );
}
(*this).add(temp_set);
temp_set.uninit();
}
void remove(set set1) {
(*this).init();
for(int i=0;i<m_size;i++) {
if( (*(m_elements+i)).is_eq(set1) ) {
set* temp = new set[m_size-1];
for(int j=0;j<m_size;j++) {
if(j<i) {
*(temp+j)=*(m_elements+j);
}
else if(j>i) {
*(temp+j-1)=*(m_elements+j);
}
}
delete[] m_elements;
m_size--;
m_elements = new set[m_size];
for(int j=0;j<m_size;j++) {
*(m_elements+j)= *(temp+j);
}
delete[] temp;
break;
}
}
}
void join(set set1) {
for(int i=0;i<set1.size();i++) {
(*this).add( *(set1.elements()+i) );
}
}
void diff(set set1) {
for(int i=0;i<set1.size();i++) {
(*this).remove( *(set1.elements()+i) );
}
}
void intersect(set set1) {
for(int i=0;i<m_size;i++) {
bool keep = false;
for(int j=0;j<set1.size();j++) {
if( (*(m_elements+i)).is_eq( *(set1.elements()+j) ) ) {
keep = true;
break;
}
}
if(!keep) {
(*this).remove( *(m_elements+i) );
}
}
}
void natural(long number) {
//////////////////////////
//MEMORY LEAK?
//delete[] m_elements;
/////////////////////////
m_size = 0;
m_elements = new set[m_size];
for(long i=1;i<=number;i++) {
(*this).add_self();
}
m_value = number;
}
void disp() {
if( m_size==0) {cout<<"{}";}
else {
cout<<"{";
for(int i=0; i<m_size; i++) {
(*(m_elements+i)).disp();
if(i<m_size-1) {cout<<", ";}
//else{cout<<" ";}
}
cout<<"}";
}
}
long value() {
return m_value;
}
};
const set EMPTY_SET;
interpreter.cpp
#include<fstream>
#include<iostream>
#include<string>
#include<assert.h>
#include<cmath>
#include "headers/set.h"
using namespace std;
string labels[20];
int jump_points[20];
int label_index=0;
const int max_var = 20;
set* set_ptrs[max_var];
string set_names[max_var];
long OPERATIONS = 0;
void assign_var(string name, set other_set) {
static int index = 0;
bool exists = false;
int i = 0;
while(i<index) {
if(name==set_names[i]) {
exists = true;
break;
}
i++;
}
if(exists && index<max_var) {
*(set_ptrs[i]) = other_set;
}
else if(!exists && index<max_var) {
set_ptrs[index] = new set;
*(set_ptrs[index]) = other_set;
set_names[index] = name;
index++;
}
}
int getJumpPoint(string str) {
for(int i=0;i<label_index;i++) {
//cout<<labels[i]<<"\n";
if(labels[i]==str) {
//cout<<jump_points[i];
return jump_points[i];
}
}
cerr<<"Invalid Label Name: '"<<str<<"'\n";
//assert(0);
return -1;
}
long strToLong(string str) {
long j=str.size()-1;
long value = 0;
for(long i=0;i<str.size();i++) {
long x = str[i]-48;
assert(x>=0 && x<=9); // Crash if there was a non digit character
value+=x*floor( pow(10,j) );
j--;
}
return value;
}
long getValue(string str) {
for(int i=0;i<max_var;i++) {
if(set_names[i]==str) {
set set1;
set1.natural( (*(set_ptrs[i])).size() );
if( set1.is_eq( *(set_ptrs[i]) ) ) {
return (*(set_ptrs[i])).size();
}
else {
cerr<<"That is not a valid integer construction";
return 0;
}
}
}
return strToLong(str);
}
int main(int argc, char** argv){
if(argc<2){std::cerr<<"No input file given"; return 1;}
ifstream inf(argv[1]);
if(!inf){std::cerr<<"File open failed";return 1;}
assign_var("ANSWER", EMPTY_SET);
int answer;
string str;
inf>>str;
if(str=="*") {
inf>>str;
long a = strToLong(str);
inf>>str;
long b = strToLong(str);
answer = a*b;
set set1; set set2;
set1.natural(a); set2.natural(b);
assign_var("set1", set1);
assign_var("set2",set2);
//cout<<answer;
}
else if(str=="+") {
inf>>str;
long a = strToLong(str);
inf>>str;
long b = strToLong(str);
answer = a+b;
set set1; set set2;
set1.natural(a); set2.natural(b);
assign_var("set1", set1);
assign_var("set2",set2);
//cout<<answer;
}
else{
cerr<<"file must start with '+' or '*'";
return 1;
}
// parse for labels
while(inf) {
if(inf) {
inf>>str;
if(str[str.size()-1]==':') {
str.erase(str.size()-1);
labels[label_index] = str;
jump_points[label_index] = inf.tellg();
//cout<<str<<": "<<jump_points[label_index]<<"\n";
label_index++;
OPERATIONS++;
}
}
}
inf.clear();
inf.seekg(0,ios::beg);
// parse for everything else
while(inf) {
if(inf) {
inf>>str;
if(str==";") {
getline(inf, str,'\n');
}
// jump label
if(str=="jump") {
inf>>str;
inf.seekg( getJumpPoint(str),ios::beg);
OPERATIONS++;
}
// je set label
if(str=="je") {
inf>>str;
for(int i=0;i<max_var;i++) {
if( set_names[i]==str) {
if( (*(set_ptrs[i])).is_eq(EMPTY_SET) ) {
inf>>str;
inf.seekg( getJumpPoint(str),ios::beg);
OPERATIONS++;
}
break;
}
}
}
// jne set label
if(str=="jne") {
inf>>str;
for(int i=0;i<max_var;i++) {
if( set_names[i]==str) {
if(! (*(set_ptrs[i])).is_eq(EMPTY_SET) ) {
inf>>str;
inf.seekg( getJumpPoint(str),ios::beg);
OPERATIONS++;
}
break;
}
}
}
// jic set1 set2 label
// jump if set1 contains set2
if(str=="jic") {
inf>>str;
string str2;
inf>>str2;
set set1;
set set2;
for(int i=0;i<max_var;i++) {
if( set_names[i]==str ) {
set1 = *(set_ptrs[i]);
}
if(set_names[i]==str2) {
set2 = *(set_ptrs[i]);
}
}
if( set1.contains(set2) ) {
inf>>str;
inf.seekg( getJumpPoint(str),ios::beg);
OPERATIONS++;
}
else {inf>>str;}
}
// jidc set1 set2 label
// jump if set1 doesn't contain set2
if(str=="jidc") {
inf>>str;
string str2;
inf>>str2;
set set1;
set set2;
for(int i=0;i<max_var;i++) {
if( set_names[i]==str ) {
set1 = *(set_ptrs[i]);
}
if(set_names[i]==str2) {
set2 = *(set_ptrs[i]);
}
}
if( !set1.contains(set2) ) {
inf>>str;
inf.seekg( getJumpPoint(str),ios::beg);
OPERATIONS++;
}
else {inf>>str;}
}
// assign variable set/int
if(str=="assign") {
inf>>str;
string str2;
inf>>str2;
set set1;
set1.natural( getValue(str2) );
assign_var(str,set1);
OPERATIONS++;
}
// union set1 set2 set3
// set1 = set2 u set3
if(str=="union") {
inf>>str;
int i=0;
while(i<max_var) {
if( set_names[i] == str ) {
break;
}
i++;
}
set set1;
set set2;
string str1;
inf>>str1;
string str2;
inf>>str2;
for(int j=0;j<max_var;j++) {
if( str1 == set_names[j] ) {
set1= *(set_ptrs[j]);
}
if( str2 == set_names[j] ) {
set2= *(set_ptrs[j]);
}
}
set1.join(set2);
if(i==max_var) {
assign_var(str,set1);
}
else {
set_names[i]= str;
set_ptrs[i] = new set;
*(set_ptrs[i]) = set1;
}
OPERATIONS++;
}
// intersect set1 set2 set3
// set1 = set2^set3
if(str == "intersect") {
inf>>str;
int i=0;
while(i<max_var) {
if( set_names[i] == str ) {
break;
}
i++;
}
set set1;
set set2;
string str1;
inf>>str1;
string str2;
inf>>str2;
for(int j=0;j<max_var;j++) {
if( str1 == set_names[j] ) {
set1= *(set_ptrs[j]);
}
if( str2 == set_names[j] ) {
set2= *(set_ptrs[j]);
}
}
set1.intersect(set2);
if(i==max_var) {
assign_var(str,set1);
}
else {
set_names[i]= str;
set_ptrs[i] = new set;
*(set_ptrs[i]) = set1;
}
OPERATIONS++;
}
// difference set1 set2 set3
// set1 = set2\set3
if(str == "difference") {
inf>>str;
int i=0;
while(i<max_var) {
if( set_names[i] == str ) {
break;
}
i++;
}
set set1;
set set2;
string str1;
inf>>str1;
string str2;
inf>>str2;
for(int j=0;j<max_var;j++) {
if( str1 == set_names[j] ) {
set1= *(set_ptrs[j]);
}
if( str2 == set_names[j] ) {
set2= *(set_ptrs[j]);
}
}
set1.diff(set2);
if(i==max_var) {
assign_var(str,set1);
}
else {
set_names[i]= str;
set_ptrs[i] = new set;
*(set_ptrs[i]) = set1;
}
OPERATIONS++;
}
// add set1 set2
// put set2 in set 1
if(str=="add") {
inf>>str;
int i = 0; int j =0;
while(i<max_var) {
if(set_names[i]==str) {
break;
}
i++;
}
inf>>str;
while(j<max_var) {
if(set_names[j]==str) {
break;
}
j++;
}
set set2 = *(set_ptrs[j]);
if( ! (*(set_ptrs[i])).is_eq(set2) ){
(*(set_ptrs[i])).add(set2);
}
else {
(*(set_ptrs[i])).add_self();
}
OPERATIONS++;
}
// remove set1 set2
// remove set2 from set1
if(str=="remove") {
inf>>str;
int i = 0; int j =0;
while(i<max_var) {
if(set_names[i]==str) {
break;
}
i++;
}
inf>>str;
while(j<max_var) {
if(set_names[j]==str) {
break;
}
j++;
}
set set2 = *(set_ptrs[j]);
(*(set_ptrs[i])).remove(set2);
OPERATIONS++;
}
// print set
// prints true representation of set
if(str=="print") {
inf>>str;
for(int i=0;i<max_var;i++) {
if(set_names[i]==str) {
(*(set_ptrs[i])).disp();
}
}
cout<<"\n";
}
// printi set
// prints integer representation of set, if exists.
if(str=="printi") {
inf>>str;
cout<<getValue(str);
cout<<"\n";
}
}
}
cout<<"You used "<<OPERATIONS<<" operations\n";
set testset;
testset.natural(answer);
switch( testset.is_eq( *(set_ptrs[0]) ) ) {
case 1:
cout<<"Your answer is correct, the set 'ANSWER' is equivalent "<<answer<<".\n";
break;
case 0:
cout<<"Your answer is incorrect\n";
}
// cout<<"\n";
return 0;
}
Gewinnbedingung
Sie sind zwei schreiben zwei Programmkörper , von denen einer die Zahlen in den Überschriften multipliziert, der andere die Zahlen in den Überschriften addiert.
Dies ist eine Herausforderung mit dem schnellsten Code . Was am schnellsten ist, wird durch die Anzahl der Operationen bestimmt, die zum Lösen von zwei Testfällen für jedes Programm verwendet werden. Die Testfälle sind die folgenden Kopfzeilen:
Zur Ergänzung:
+ 15 12
und
+ 12 15
und zur Multiplikation
* 4 5
und
* 5 4
Eine Punktzahl für jeden Fall ist die Anzahl der verwendeten Operationen (der Interpreter gibt diese Anzahl nach Abschluss des Programms an). Die Gesamtpunktzahl ist die Summe der Punktzahlen für jeden Testfall.
In meinem Beispieleintrag finden Sie ein Beispiel für einen gültigen Eintrag.
Eine Gewinner-Einsendung erfüllt folgende Anforderungen:
- enthält zwei Programmkörper, einen multiplizierenden und einen addierenden
- hat die niedrigste Gesamtpunktzahl (Summe der Punktzahlen in Testfällen)
- Funktioniert bei ausreichender Zeit und ausreichendem Speicher für jede Ganzzahl, die vom Interpreter verarbeitet werden kann (~ 2 ^ 31).
- Zeigt beim Ausführen keine Fehler an
- Verwendet keine Debugging-Befehle
- Nutzt keine Fehler im Dolmetscher aus. Dies bedeutet, dass Ihr aktuelles Programm sowohl als Pseudocode als auch als interpretierbares Programm in 'eingestellter Sprache' gültig sein sollte.
- Nutzt keine Standardlücken (dies bedeutet, dass keine Testfälle fest codiert werden).
In meinem Beispiel finden Sie eine Referenzimplementierung und eine Beispielverwendung der Sprache.
$$...$$
funktioniert auf Meta, aber nicht auf Main. Ich habe CodeCogs verwendet , um die Bilder zu generieren.