Définir l'arithmétique théorique
Prémisse
Il y a déjà eu quelques défis qui impliquent la multiplication sans l'opérateur de multiplication ( ici et ici ) et ce défi est dans la même veine (le plus similaire au deuxième lien).
Ce défi, contrairement aux précédents, utilisera une définition théorique définie des nombres naturels ( N ):
et
par exemple,
etc.
Le défi
Notre objectif est d'utiliser des opérations d'ensemble (voir ci-dessous), pour ajouter et multiplier des nombres naturels. À cet effet, toutes les entrées seront dans la même «langue définie» dont l'interprète est ci-dessous . Cela assurera la cohérence et facilitera la notation.
Cet interpréteur vous permet de manipuler des nombres naturels sous forme d'ensembles. Votre tâche sera d'écrire deux corps de programme (voir ci-dessous), dont l'un ajoute des nombres naturels, l'autre les multiplie.
Notes préliminaires sur les décors
Les ensembles suivent la structure mathématique habituelle. Voici quelques points importants:
- Les ensembles ne sont pas commandés.
- Aucun ensemble ne se contient
- Les éléments sont dans un ensemble ou non, c'est booléen. Par conséquent, les éléments d'ensemble ne peuvent pas avoir de multiplicités (c'est-à-dire qu'un élément ne peut pas être dans un ensemble plusieurs fois.)
Interprète et spécificités
Un «programme» pour ce défi est écrit en «langue définie» et se compose de deux parties: un en-tête et un corps.
Entête
L'en-tête est très simple. Il indique à l'interprète le programme que vous résolvez. L'en-tête est la première ligne du programme. Il commence par le caractère +
ou *
, suivi de deux entiers, séparés par des espaces. Par exemple:
+ 3 5
ou
* 19 2
sont des en-têtes valides. Le premier indique que vous essayez de résoudre 3+5
, ce qui signifie que votre réponse devrait être 8
. Le second est similaire sauf avec la multiplication.
Corps
Le corps est l'endroit où se trouvent réellement vos instructions à l'interprète. C'est ce qui constitue vraiment votre programme «d'addition» ou de «multiplication». Votre réponse comprendra deux corps de programme, un pour chaque tâche. Vous modifierez ensuite les en-têtes pour effectuer réellement les cas de test.
Syntaxe et instructions
Les instructions consistent en une commande suivie de zéro ou plusieurs paramètres. Aux fins des démonstrations suivantes, tout caractère alphabétique est le nom d'une variable. Rappelons que toutes les variables sont des ensembles. label
est le nom d'une étiquette (les étiquettes sont des mots suivis de points-virgules (c.-à-d. main_loop:
), int
est un entier. Voici les instructions valides:
jump label
sauter sans condition à l'étiquette. Une étiquette est un «mot» suivi d'un point-virgule: par exemple,main_loop:
est une étiquette.je A label
passer à l'étiquette si A est videjne A label
passer à l'étiquette si A n'est pas videjic A B label
passer à l'étiquette si A contient Bjidc A B label
passer à l'étiquette si A ne contient pas B
print A
affiche la vraie valeur de A, où {} est l'ensemble videprinti variable
affiche une représentation entière de A, si elle existe, sinon génère une erreur.
;
Le point-virgule indique que le reste de la ligne est un commentaire et sera ignoré par l'interpréteur
Plus d'infos
Au démarrage du programme, il existe trois variables préexistantes. Ils sont set1
,set2
et ANSWER
. set1
prend la valeur du premier paramètre d'en-tête. set2
prend la valeur de la seconde. ANSWER
est initialement l'ensemble vide. À la fin du programme, l'interpréteur vérifie si ANSWER
est la représentation entière de la réponse au problème arithmétique défini dans l'en-tête. Si c'est le cas, il l'indique avec un message à stdout.
L'interprète affiche également le nombre d'opérations utilisées. Chaque instruction est une opération. Le lancement d'une étiquette coûte également une opération (les étiquettes ne peuvent être lancées qu'une seule fois).
Vous pouvez avoir un maximum de 20 variables (y compris les 3 variables prédéfinies) et 20 étiquettes.
Code interprète
NOTES IMPORTANTES SUR CET INTERPRÈTELes choses sont très lentes lors de l'utilisation de grands nombres (> 30) dans cet interprète. Je vais en expliquer les raisons.
- La structure des ensembles est telle qu'en augmentant d'un nombre naturel, vous doublez effectivement la taille de la structure de l'ensemble. Le n ème nombre naturel contient 2 ^ n ensembles vides (j'entends par là que si vous regardez n comme un arbre, il y a n ensembles vides. Notez que seuls les ensembles vides peuvent être des feuilles.) Cela signifie que traiter 30 est significativement plus coûteux que de traiter 20 ou 10 (vous regardez 2 ^ 10 vs 2 ^ 20 vs 2 ^ 30).
- Les contrôles d'égalité sont récursifs. Étant donné que les décors ne seraient pas commandés, cela semblait être le moyen naturel de résoudre ce problème.
- Il y a deux fuites de mémoire que je n'ai pas réussi à résoudre. Je suis mauvais en C / C ++, désolé. Étant donné que nous ne traitons que de petits nombres et que la mémoire allouée est libérée à la fin du programme, cela ne devrait pas vraiment être un problème majeur. (Avant que quelqu'un ne dise quoi que ce soit, oui je le sais
std::vector
; je faisais cela comme un exercice d'apprentissage. Si vous savez comment le réparer, faites-le moi savoir et je ferai les modifications, sinon, puisque cela fonctionne, je le laisserai comme si.)
Notez également le chemin d'accès inclus set.h
dans le interpreter.cpp
fichier. Sans plus tarder, le code source (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;
}
Condition gagnante
Vous êtes deux, écrivez deux CORPS de programme , dont l'un multiplie les nombres dans les en-têtes, dont l'autre ajoute les nombres dans les en-têtes.
Il s'agit d'un défi de code le plus rapide . Ce qui est le plus rapide sera déterminé par le nombre d'opérations utilisées pour résoudre deux cas de test pour chaque programme. Les cas de test sont les en-têtes suivants:
Pour plus:
+ 15 12
et
+ 12 15
et pour la multiplication
* 4 5
et
* 5 4
Un score pour chaque cas est le nombre d'opérations utilisées (l'interprète indiquera ce nombre à la fin du programme). Le score total est la somme des scores de chaque scénario de test.
Voir mon exemple d'entrée pour un exemple d'entrée valide.
Une soumission gagnante satisfait aux critères suivants:
- contient deux organes de programme, un qui multiplie et un qui ajoute
- a le score total le plus bas (somme des scores dans les cas de test)
- Avec suffisamment de temps et de mémoire, fonctionne pour tout entier pouvant être traité par l'interpréteur (~ 2 ^ 31)
- N'affiche aucune erreur lors de l'exécution
- N'utilise pas de commandes de débogage
- N'exploite pas les défauts de l'interpréteur. Cela signifie que votre programme réel doit être valide en tant que pseudo-code ainsi qu'en tant que programme interprétable en «langue définie».
- N'exploite pas les failles standard (cela signifie pas de cas de test de codage en dur.)
Veuillez consulter mon exemple pour une implémentation de référence et un exemple d'utilisation du langage.
$$...$$
fonctionne sur Meta, mais pas sur Main. J'ai utilisé CodeCogs pour générer les images.