This section includes source code for all of the Gurobi sudoku examples.The same source code can be found in theexamples directory of theGurobi distribution.
/* Copyright 2025, Gurobi Optimization, LLC *//* Sudoku example. The Sudoku board is a 9x9 grid, which is further divided into a 3x3 grid of 3x3 grids. Each cell in the grid must take a value from 0 to 9. No two grid cells in the same row, column, or 3x3 subgrid may take the same value. In the MIP formulation, binary variables x[i,j,v] indicate whether cell <i,j> takes value 'v'. The constraints are as follows: 1. Each cell must take exactly one value (sum_v x[i,j,v] = 1) 2. Each value is used exactly once per row (sum_i x[i,j,v] = 1) 3. Each value is used exactly once per column (sum_j x[i,j,v] = 1) 4. Each value is used exactly once per 3x3 subgrid (sum_grid x[i,j,v] = 1) Input datasets for this example can be found in examples/data/sudoku*.*/#include<stdlib.h>#include<stdio.h>#include<string.h>#include"gurobi_c.h"#define SUBDIM 3#define DIM (SUBDIM*SUBDIM)intmain(intargc,char*argv[]){FILE*fp=NULL;GRBenv*env=NULL;GRBmodel*model=NULL;intboard[DIM][DIM];charinputline[100];intind[DIM];doubleval[DIM];doublelb[DIM*DIM*DIM];charvtype[DIM*DIM*DIM];char*names[DIM*DIM*DIM];charnamestorage[10*DIM*DIM*DIM];char*cursor;intoptimstatus;doubleobjval;inti,j,v,ig,jg,count;interror=0;if(argc<2){fprintf(stderr,"Usage: sudoku_c datafile\n");exit(1);}fp=fopen(argv[1],"r");if(fp==NULL){fprintf(stderr,"Error: unable to open input file %s\n",argv[1]);exit(1);}for(i=0;i<DIM;i++){fgets(inputline,100,fp);if(strlen(inputline)<9){fprintf(stderr,"Error: not enough board positions specified\n");exit(1);}for(j=0;j<DIM;j++){board[i][j]=(int)inputline[j]-(int)'1';if(board[i][j]<0||board[i][j]>=DIM)board[i][j]=-1;}}/* Create an empty model */cursor=namestorage;for(i=0;i<DIM;i++){for(j=0;j<DIM;j++){for(v=0;v<DIM;v++){if(board[i][j]==v)lb[i*DIM*DIM+j*DIM+v]=1;elselb[i*DIM*DIM+j*DIM+v]=0;vtype[i*DIM*DIM+j*DIM+v]=GRB_BINARY;names[i*DIM*DIM+j*DIM+v]=cursor;sprintf(names[i*DIM*DIM+j*DIM+v],"x[%d,%d,%d]",i,j,v+1);cursor+=strlen(names[i*DIM*DIM+j*DIM+v])+1;}}}/* Create environment */error=GRBloadenv(&env,"sudoku.log");if(error)gotoQUIT;/* Create new model */error=GRBnewmodel(env,&model,"sudoku",DIM*DIM*DIM,NULL,lb,NULL,vtype,names);if(error)gotoQUIT;/* Each cell gets a value */for(i=0;i<DIM;i++){for(j=0;j<DIM;j++){for(v=0;v<DIM;v++){ind[v]=i*DIM*DIM+j*DIM+v;val[v]=1.0;}error=GRBaddconstr(model,DIM,ind,val,GRB_EQUAL,1.0,NULL);if(error)gotoQUIT;}}/* Each value must appear once in each row */for(v=0;v<DIM;v++){for(j=0;j<DIM;j++){for(i=0;i<DIM;i++){ind[i]=i*DIM*DIM+j*DIM+v;val[i]=1.0;}error=GRBaddconstr(model,DIM,ind,val,GRB_EQUAL,1.0,NULL);if(error)gotoQUIT;}}/* Each value must appear once in each column */for(v=0;v<DIM;v++){for(i=0;i<DIM;i++){for(j=0;j<DIM;j++){ind[j]=i*DIM*DIM+j*DIM+v;val[j]=1.0;}error=GRBaddconstr(model,DIM,ind,val,GRB_EQUAL,1.0,NULL);if(error)gotoQUIT;}}/* Each value must appear once in each subgrid */for(v=0;v<DIM;v++){for(ig=0;ig<SUBDIM;ig++){for(jg=0;jg<SUBDIM;jg++){count=0;for(i=ig*SUBDIM;i<(ig+1)*SUBDIM;i++){for(j=jg*SUBDIM;j<(jg+1)*SUBDIM;j++){ind[count]=i*DIM*DIM+j*DIM+v;val[count]=1.0;count++;}}error=GRBaddconstr(model,DIM,ind,val,GRB_EQUAL,1.0,NULL);if(error)gotoQUIT;}}}/* Optimize model */error=GRBoptimize(model);if(error)gotoQUIT;/* Write model to 'sudoku.lp' */error=GRBwrite(model,"sudoku.lp");if(error)gotoQUIT;/* Capture solution information */error=GRBgetintattr(model,GRB_INT_ATTR_STATUS,&optimstatus);if(error)gotoQUIT;error=GRBgetdblattr(model,GRB_DBL_ATTR_OBJVAL,&objval);if(error)gotoQUIT;printf("\nOptimization complete\n");if(optimstatus==GRB_OPTIMAL)printf("Optimal objective: %.4e\n",objval);elseif(optimstatus==GRB_INF_OR_UNBD)printf("Model is infeasible or unbounded\n");elseprintf("Optimization was stopped early\n");printf("\n");QUIT:/* Error reporting */if(error){printf("ERROR: %s\n",GRBgeterrormsg(env));exit(1);}fclose(fp);/* Free model */GRBfreemodel(model);/* Free environment */GRBfreeenv(env);return0;}
/* Copyright 2025, Gurobi Optimization, LLC *//* Sudoku example. The Sudoku board is a 9x9 grid, which is further divided into a 3x3 grid of 3x3 grids. Each cell in the grid must take a value from 0 to 9. No two grid cells in the same row, column, or 3x3 subgrid may take the same value. In the MIP formulation, binary variables x[i,j,v] indicate whether cell <i,j> takes value 'v'. The constraints are as follows: 1. Each cell must take exactly one value (sum_v x[i,j,v] = 1) 2. Each value is used exactly once per row (sum_i x[i,j,v] = 1) 3. Each value is used exactly once per column (sum_j x[i,j,v] = 1) 4. Each value is used exactly once per 3x3 subgrid (sum_grid x[i,j,v] = 1) Input datasets for this example can be found in examples/data/sudoku*.*/#include"gurobi_c++.h"#include<sstream>usingnamespacestd;#define sd 3#define n (sd*sd)stringitos(inti){stringstreams;s<<i;returns.str();}intmain(intargc,char*argv[]){try{GRBEnvenv=GRBEnv();GRBModelmodel=GRBModel(env);GRBVarvars[n][n][n];inti,j,v;// Create 3-D array of model variablesfor(i=0;i<n;i++){for(j=0;j<n;j++){for(v=0;v<n;v++){strings="G_"+itos(i)+"_"+itos(j)+"_"+itos(v);vars[i][j][v]=model.addVar(0.0,1.0,0.0,GRB_BINARY,s);}}}// Add constraints// Each cell must take one valuefor(i=0;i<n;i++){for(j=0;j<n;j++){GRBLinExprexpr=0;for(v=0;v<n;v++)expr+=vars[i][j][v];strings="V_"+itos(i)+"_"+itos(j);model.addConstr(expr,GRB_EQUAL,1.0,s);}}// Each value appears once per rowfor(i=0;i<n;i++){for(v=0;v<n;v++){GRBLinExprexpr=0;for(j=0;j<n;j++)expr+=vars[i][j][v];strings="R_"+itos(i)+"_"+itos(v);model.addConstr(expr==1.0,s);}}// Each value appears once per columnfor(j=0;j<n;j++){for(v=0;v<n;v++){GRBLinExprexpr=0;for(i=0;i<n;i++)expr+=vars[i][j][v];strings="C_"+itos(j)+"_"+itos(v);model.addConstr(expr==1.0,s);}}// Each value appears once per sub-gridfor(v=0;v<n;v++){for(inti0=0;i0<sd;i0++){for(intj0=0;j0<sd;j0++){GRBLinExprexpr=0;for(inti1=0;i1<sd;i1++){for(intj1=0;j1<sd;j1++){expr+=vars[i0*sd+i1][j0*sd+j1][v];}}strings="Sub_"+itos(v)+"_"+itos(i0)+"_"+itos(j0);model.addConstr(expr==1.0,s);}}}// Fix variables associated with pre-specified cellscharinput[10];for(i=0;i<n;i++){cin>>input;for(j=0;j<n;j++){intval=(int)input[j]-48-1;// 0-basedif(val>=0)vars[i][j][val].set(GRB_DoubleAttr_LB,1.0);}}// Optimize modelmodel.optimize();// Write model to filemodel.write("sudoku.lp");cout<<endl;for(i=0;i<n;i++){for(j=0;j<n;j++){for(v=0;v<n;v++){if(vars[i][j][v].get(GRB_DoubleAttr_X)>0.5)cout<<v+1;}}cout<<endl;}cout<<endl;}catch(GRBExceptione){cout<<"Error code = "<<e.getErrorCode()<<endl;cout<<e.getMessage()<<endl;}catch(...){cout<<"Error during optimization"<<endl;}return0;}
/* Copyright 2025, Gurobi Optimization, LLC *//* Sudoku example. The Sudoku board is a 9x9 grid, which is further divided into a 3x3 grid of 3x3 grids. Each cell in the grid must take a value from 0 to 9. No two grid cells in the same row, column, or 3x3 subgrid may take the same value. In the MIP formulation, binary variables x[i,j,v] indicate whether cell <i,j> takes value 'v'. The constraints are as follows: 1. Each cell must take exactly one value (sum_v x[i,j,v] = 1) 2. Each value is used exactly once per row (sum_i x[i,j,v] = 1) 3. Each value is used exactly once per column (sum_j x[i,j,v] = 1) 4. Each value is used exactly once per 3x3 subgrid (sum_grid x[i,j,v] = 1) Input datasets for this example can be found in examples/data/sudoku*.*/usingSystem;usingSystem.IO;usingGurobi;classsudoku_cs{staticvoidMain(string[]args){intn=9;ints=3;if(args.Length<1){Console.Out.WriteLine("Usage: sudoku_cs filename");return;}try{GRBEnvenv=newGRBEnv();GRBModelmodel=newGRBModel(env);// Create 3-D array of model variablesGRBVar[,,]vars=newGRBVar[n,n,n];for(inti=0;i<n;i++){for(intj=0;j<n;j++){for(intv=0;v<n;v++){stringst="G_"+i.ToString()+"_"+j.ToString()+"_"+v.ToString();vars[i,j,v]=model.AddVar(0.0,1.0,0.0,GRB.BINARY,st);}}}// Add constraintsGRBLinExprexpr;// Each cell must take one valuefor(inti=0;i<n;i++){for(intj=0;j<n;j++){expr=0.0;for(intv=0;v<n;v++)expr.AddTerm(1.0,vars[i,j,v]);stringst="V_"+i.ToString()+"_"+j.ToString();model.AddConstr(expr==1.0,st);}}// Each value appears once per rowfor(inti=0;i<n;i++){for(intv=0;v<n;v++){expr=0.0;for(intj=0;j<n;j++)expr.AddTerm(1.0,vars[i,j,v]);stringst="R_"+i.ToString()+"_"+v.ToString();model.AddConstr(expr==1.0,st);}}// Each value appears once per columnfor(intj=0;j<n;j++){for(intv=0;v<n;v++){expr=0.0;for(inti=0;i<n;i++)expr.AddTerm(1.0,vars[i,j,v]);stringst="C_"+j.ToString()+"_"+v.ToString();model.AddConstr(expr==1.0,st);}}// Each value appears once per sub-gridfor(intv=0;v<n;v++){for(inti0=0;i0<s;i0++){for(intj0=0;j0<s;j0++){expr=0.0;for(inti1=0;i1<s;i1++){for(intj1=0;j1<s;j1++){expr.AddTerm(1.0,vars[i0*s+i1,j0*s+j1,v]);}}stringst="Sub_"+v.ToString()+"_"+i0.ToString()+"_"+j0.ToString();model.AddConstr(expr==1.0,st);}}}// Fix variables associated with pre-specified cellsStreamReadersr=File.OpenText(args[0]);for(inti=0;i<n;i++){stringinput=sr.ReadLine();for(intj=0;j<n;j++){intval=(int)input[j]-48-1;// 0-basedif(val>=0)vars[i,j,val].LB=1.0;}}// Optimize modelmodel.Optimize();// Write model to filemodel.Write("sudoku.lp");double[,,]x=model.Get(GRB.DoubleAttr.X,vars);Console.WriteLine();for(inti=0;i<n;i++){for(intj=0;j<n;j++){for(intv=0;v<n;v++){if(x[i,j,v]>0.5){Console.Write(v+1);}}}Console.WriteLine();}// Dispose of model and envmodel.Dispose();env.Dispose();}catch(GRBExceptione){Console.WriteLine("Error code: "+e.ErrorCode+". "+e.Message);}}}
/* Copyright 2025, Gurobi Optimization, LLC *//* Sudoku example. The Sudoku board is a 9x9 grid, which is further divided into a 3x3 grid of 3x3 grids. Each cell in the grid must take a value from 0 to 9. No two grid cells in the same row, column, or 3x3 subgrid may take the same value. In the MIP formulation, binary variables x[i,j,v] indicate whether cell <i,j> takes value 'v'. The constraints are as follows: 1. Each cell must take exactly one value (sum_v x[i,j,v] = 1) 2. Each value is used exactly once per row (sum_i x[i,j,v] = 1) 3. Each value is used exactly once per column (sum_j x[i,j,v] = 1) 4. Each value is used exactly once per 3x3 subgrid (sum_grid x[i,j,v] = 1) Input datasets for this example can be found in examples/data/sudoku*.*/importcom.gurobi.gurobi.*;importjava.io.*;publicclassSudoku{publicstaticvoidmain(String[]args){intn=9;ints=3;if(args.length<1){System.out.println("Usage: java Sudoku filename");System.exit(1);}try{GRBEnvenv=newGRBEnv();GRBModelmodel=newGRBModel(env);// Create 3-D array of model variablesGRBVar[][][]vars=newGRBVar[n][n][n];for(inti=0;i<n;i++){for(intj=0;j<n;j++){for(intv=0;v<n;v++){Stringst="G_"+String.valueOf(i)+"_"+String.valueOf(j)+"_"+String.valueOf(v);vars[i][j][v]=model.addVar(0.0,1.0,0.0,GRB.BINARY,st);}}}// Add constraintsGRBLinExprexpr;// Each cell must take one valuefor(inti=0;i<n;i++){for(intj=0;j<n;j++){expr=newGRBLinExpr();expr.addTerms(null,vars[i][j]);Stringst="V_"+String.valueOf(i)+"_"+String.valueOf(j);model.addConstr(expr,GRB.EQUAL,1.0,st);}}// Each value appears once per rowfor(inti=0;i<n;i++){for(intv=0;v<n;v++){expr=newGRBLinExpr();for(intj=0;j<n;j++)expr.addTerm(1.0,vars[i][j][v]);Stringst="R_"+String.valueOf(i)+"_"+String.valueOf(v);model.addConstr(expr,GRB.EQUAL,1.0,st);}}// Each value appears once per columnfor(intj=0;j<n;j++){for(intv=0;v<n;v++){expr=newGRBLinExpr();for(inti=0;i<n;i++)expr.addTerm(1.0,vars[i][j][v]);Stringst="C_"+String.valueOf(j)+"_"+String.valueOf(v);model.addConstr(expr,GRB.EQUAL,1.0,st);}}// Each value appears once per sub-gridfor(intv=0;v<n;v++){for(inti0=0;i0<s;i0++){for(intj0=0;j0<s;j0++){expr=newGRBLinExpr();for(inti1=0;i1<s;i1++){for(intj1=0;j1<s;j1++){expr.addTerm(1.0,vars[i0*s+i1][j0*s+j1][v]);}}Stringst="Sub_"+String.valueOf(v)+"_"+String.valueOf(i0)+"_"+String.valueOf(j0);model.addConstr(expr,GRB.EQUAL,1.0,st);}}}// Fix variables associated with pre-specified cellsFilefile=newFile(args[0]);try(BufferedReaderbr=newBufferedReader(newFileReader(file))){for(inti=0;i<n;i++){Stringline=br.readLine();for(intj=0;j<n;j++){charc=line.charAt(j);if(Character.isDigit(c)){vars[i][j][Character.getNumericValue(c)-1].set(GRB.DoubleAttr.LB,1.0);}}}}// Optimize modelmodel.optimize();// Write model to filemodel.write("sudoku.lp");double[][][]x=model.get(GRB.DoubleAttr.X,vars);System.out.println();for(inti=0;i<n;i++){for(intj=0;j<n;j++){for(intv=0;v<n;v++){if(x[i][j][v]>0.5){System.out.print(v+1);}}}System.out.println();}// Dispose of model and environmentmodel.dispose();env.dispose();}catch(GRBExceptione){System.out.println("Error code: "+e.getErrorCode()+". "+e.getMessage());}catch(IOExceptione){System.out.println("IO Error");}}}
functionsudoku(filename)% Copyright 2025, Gurobi Optimization, LLC */%% Sudoku example.%% The Sudoku board is a 9x9 grid, which is further divided into a 3x3 grid% of 3x3 grids. Each cell in the grid must take a value from 0 to 9.% No two grid cells in the same row, column, or 3x3 subgrid may take the% same value.%% In the MIP formulation, binary variables x[i,j,v] indicate whether% cell <i,j> takes value 'v'. The constraints are as follows:% 1. Each cell must take exactly one value (sum_v x[i,j,v] = 1)% 2. Each value is used exactly once per row (sum_i x[i,j,v] = 1)% 3. Each value is used exactly once per column (sum_j x[i,j,v] = 1)% 4. Each value is used exactly once per 3x3 subgrid (sum_grid x[i,j,v] = 1)%% Input datasets for this example can be found in examples/data/sudoku*.%SUBDIM=3;DIM=SUBDIM*SUBDIM;fileID=fopen(filename);iffileID==-1fprintf('Could not read file %s, quit\n',filename);return;endboard=repmat(-1,DIM,DIM);fori=1:DIMs=fgets(fileID,100);iflength(s)<=DIMfprintf('Error: not enough board positions specified, quit\n');return;endforj=1:DIMifs(j)~='.'board(i,j)=str2double(s(j));ifboard(i,j)<1||board(i,j)>DIMfprintf('Error: Unexpected character in Input line %d, quit\n',i);return;endendendend% Map X(i,j,k) into an index variable in the modelnVars=DIM*DIM*DIM;% Build modelmodel.vtype=repmat('B',nVars,1);model.lb=zeros(nVars,1);model.ub=ones(nVars,1);fori=1:DIMforj=1:DIMforv=1:DIMvar=(i-1)*DIM*DIM+(j-1)*DIM+v;model.varnames{var}=sprintf('x[%d,%d,%d]',i,j,v);endendend% Create constraints:nRows=4*DIM*DIM;model.A=sparse(nRows,nVars);model.rhs=ones(nRows,1);model.sense=repmat('=',nRows,1);Row=1;% Each cell gets a value */fori=1:DIMforj=1:DIMforv=1:DIMifboard(i,j)==vmodel.lb((i-1)*DIM*DIM+(j-1)*DIM+v)=1;endmodel.A(Row,(i-1)*DIM*DIM+(j-1)*DIM+v)=1;endRow=Row+1;endend% Each value must appear once in each rowforv=1:DIMforj=1:DIMfori=1:DIMmodel.A(Row,(i-1)*DIM*DIM+(j-1)*DIM+v)=1;endRow=Row+1;endend% Each value must appear once in each columnforv=1:DIMfori=1:DIMforj=1:DIMmodel.A(Row,(i-1)*DIM*DIM+(j-1)*DIM+v)=1;endRow=Row+1;endend% Each value must appear once in each subgridforv=1:DIMforig=0:SUBDIM-1forjg=0:SUBDIM-1fori=ig*SUBDIM+1:(ig+1)*SUBDIMforj=jg*SUBDIM+1:(jg+1)*SUBDIMmodel.A(Row,(i-1)*DIM*DIM+(j-1)*DIM+v)=1;endendRow=Row+1;endendend% Save modelgurobi_write(model,'sudoku_m.lp');% Optimize modelparams.logfile='sudoku_m.log';result=gurobi(model,params);ifstrcmp(result.status,'OPTIMAL')fprintf('Solution:\n');fori=1:DIMforj=1:DIMforv=1:DIMvar=(i-1)*DIM*DIM+(j-1)*DIM+v;ifresult.x(var)>0.99fprintf('%d',v);endendendfprintf('\n');endelsefprintf('Problem was infeasible\n')end
#!/usr/bin/env python3# Copyright 2025, Gurobi Optimization, LLC# Sudoku example.# The Sudoku board is a 9x9 grid, which is further divided into a 3x3 grid# of 3x3 grids. Each cell in the grid must take a value from 0 to 9.# No two grid cells in the same row, column, or 3x3 subgrid may take the# same value.## In the MIP formulation, binary variables x[i,j,v] indicate whether# cell <i,j> takes value 'v'. The constraints are as follows:# 1. Each cell must take exactly one value (sum_v x[i,j,v] = 1)# 2. Each value is used exactly once per row (sum_i x[i,j,v] = 1)# 3. Each value is used exactly once per column (sum_j x[i,j,v] = 1)# 4. Each value is used exactly once per 3x3 subgrid (sum_grid x[i,j,v] = 1)## Input datasets for this example can be found in examples/data/sudoku*.importsysimportmathimportgurobipyasgpfromgurobipyimportGRBiflen(sys.argv)<2:print("Usage: sudoku.py filename")sys.exit(0)f=open(sys.argv[1])grid=f.read().split()n=len(grid[0])s=int(math.sqrt(n))# Create our 3-D array of model variablesmodel=gp.Model("sudoku")vars=model.addVars(n,n,n,vtype=GRB.BINARY,name="G")# Fix variables associated with cells whose values are pre-specifiedforiinrange(n):forjinrange(n):ifgrid[i][j]!=".":v=int(grid[i][j])-1vars[i,j,v].LB=1# Each cell must take one valuemodel.addConstrs((vars.sum(i,j,"*")==1foriinrange(n)forjinrange(n)),name="V")# Each value appears once per rowmodel.addConstrs((vars.sum(i,"*",v)==1foriinrange(n)forvinrange(n)),name="R")# Each value appears once per columnmodel.addConstrs((vars.sum("*",j,v)==1forjinrange(n)forvinrange(n)),name="C")# Each value appears once per subgridmodel.addConstrs((gp.quicksum(vars[i,j,v]foriinrange(i0*s,(i0+1)*s)forjinrange(j0*s,(j0+1)*s))==1forvinrange(n)fori0inrange(s)forj0inrange(s)),name="Sub",)model.optimize()model.write("sudoku.lp")print("")print("Solution:")print("")# Retrieve optimization resultsolution=model.getAttr("X",vars)foriinrange(n):sol=""forjinrange(n):forvinrange(n):ifsolution[i,j,v]>0.5:sol+=str(v+1)print(sol)
# Copyright 2025, Gurobi Optimization, LLC */## Sudoku example.## The Sudoku board is a 9x9 grid, which is further divided into a 3x3 grid# of 3x3 grids. Each cell in the grid must take a value from 0 to 9.# No two grid cells in the same row, column, or 3x3 subgrid may take the# same value.## In the MIP formulation, binary variables x[i,j,v] indicate whether# cell <i,j> takes value 'v'. The constraints are as follows:# 1. Each cell must take exactly one value (sum_v x[i,j,v] = 1)# 2. Each value is used exactly once per row (sum_i x[i,j,v] = 1)# 3. Each value is used exactly once per column (sum_j x[i,j,v] = 1)# 4. Each value is used exactly once per 3x3 subgrid (sum_grid x[i,j,v] = 1)## Input datasets for this example can be found in examples/data/sudoku*.#library(Matrix)library(gurobi)args<-commandArgs(trailingOnly=TRUE)if(length(args)<1){stop('Usage: Rscript sudoku.R filename\n')}# Read input fileconn<-file(args[1],open='r')if(!isOpen(conn)){cat('Could not read file',args[1],'\n')stop('Stop now\n')}linn<-readLines(conn)close(conn)# Ensure that all lines have the same length as the number of lines, and# that the character set is the correct one.# Load fixed positions in boardDim<-length(linn)board<-matrix(0,Dim,Dim,byrow=TRUE)if(Dim!=9){cat('Input file',args[1],'has',Dim,'lines instead of 9\n')stop('Stop now\n')}for(iin1:Dim){line<-strsplit(linn[[i]],split='')[[1]]if(length(line)!=Dim){cat('Input line',i,'has',length(line),'characters, expected',Dim,'\n')stop('Stop now\n')}for(jin1:Dim){if(line[[j]]!='.'){k<-as.numeric(line[[j]])if(k<1||k>Dim){cat('Unexpected character in Input line',i,'character',j,'\n')stop('Stop now\n')}else{board[i,j]=k}}}}# Map X[i,j,k] into an index variable in the modelnVars<-Dim*Dim*DimvarIdx<-function(i,j,k){i+(j-1)*Dim+(k-1)*Dim*Dim}cat('Dataset grid:',Dim,'x',Dim,'\n')# Set up parametersparams<-list()params$logfile<-'sudoku.log'# Build modelmodel<-list()model$modelname<-'sudoku'model$modelsense<-'min'# Create variable names, types, and boundsmodel$vtype<-'B'model$lb<-rep(0,nVars)model$ub<-rep(1,nVars)model$varnames<-rep('',nVars)for(iin1:Dim){for(jin1:Dim){for(kin1:Dim){if(board[i,j]==k)model$lb[varIdx(i,j,k)]=1model$varnames[varIdx(i,j,k)]=paste0('X',i,j,k)}}}# Create (empty) constraints:model$A<-spMatrix(0,nVars)model$rhs<-c()model$sense<-c()model$constrnames<-c()# Each cell gets a value:for(iin1:Dim){for(jin1:Dim){B<-spMatrix(1,nVars,i=rep(1,Dim),j=varIdx(i,j,1:Dim),x=rep(1,Dim))model$A<-rbind(model$A,B)model$rhs<-c(model$rhs,1)model$sense<-c(model$sense,'=')model$constrnames<-c(model$constrnames,paste0('OneValInCell',i,j))}}# Each value must appear once in each columnfor(iin1:Dim){for(kin1:Dim){B<-spMatrix(1,nVars,i=rep(1,Dim),j=varIdx(i,1:Dim,k),x=rep(1,Dim))model$A<-rbind(model$A,B)model$rhs<-c(model$rhs,1)model$sense<-c(model$sense,'=')model$constrnames<-c(model$constrnames,paste0('OnceValueInRow',i,k))}}#Each value must appear once in each rowfor(jin1:Dim){for(kin1:Dim){B<-spMatrix(1,nVars,i=rep(1,Dim),j=varIdx(1:Dim,j,k),x=rep(1,Dim))model$A<-rbind(model$A,B)model$rhs<-c(model$rhs,1)model$sense<-c(model$sense,'=')model$constrnames<-c(model$constrnames,paste0('OnceValueInColumn',j,k))}}# Each value must appear once in each subgridSubDim<-3for(kin1:Dim){for(g1in1:SubDim){for(g2in1:SubDim){B<-spMatrix(1,nVars,i=rep(1,Dim),j=c(varIdx(1+(g1-1)*SubDim,(g2-1)*SubDim+1:SubDim,k),varIdx(2+(g1-1)*SubDim,(g2-1)*SubDim+1:SubDim,k),varIdx(3+(g1-1)*SubDim,(g2-1)*SubDim+1:SubDim,k)),x=rep(1,Dim))model$A<-rbind(model$A,B)model$rhs<-c(model$rhs,1)model$sense<-c(model$sense,'=')model$constrnames<-c(model$constrnames,paste0('OnceValueInSubGrid',g1,g2,k))}}}# Save modelgurobi_write(model,'sudoku.lp',params)# Optimize modelresult<-gurobi(model,params=params)if(result$status=='OPTIMAL'){cat('Solution:\n')cat('----------------------------------\n')for(iin1:Dim){for(jin1:Dim){if(j%%SubDim==1)cat('| ')for(kin1:Dim){if(result$x[varIdx(i,j,k)]>0.99){cat(k,' ')}}}cat('|\n')if(i%%SubDim==0)cat('----------------------------------\n')}}else{cat('Problem was infeasible\n')}# Clear spacerm(result,model,board,linn,params)
' Copyright 2025, Gurobi Optimization, LLC'' Sudoku example.'' The Sudoku board is a 9x9 grid, which is further divided into a 3x3 grid' of 3x3 grids. Each cell in the grid must take a value from 0 to 9.' No two grid cells in the same row, column, or 3x3 subgrid may take the' same value.' In the MIP formulation, binary variables x(i,j,v) indicate whether' cell <i,j> takes value 'v'. The constraints are as follows:' 1. Each cell must take exactly one value (sum_v x(i,j,v) = 1)' 2. Each value is used exactly once per row (sum_i x(i,j,v) = 1)' 3. Each value is used exactly once per column (sum_j x(i,j,v) = 1)' 4. Each value is used exactly once per 3x3 subgrid (sum_grid x(i,j,v) = 1)'' Input datasets for this example can be found in examples/data/sudoku*.ImportsSystemImportsSystem.IOImportsGurobiClasssudoku_vbSharedSubMain(ByValargsasString())DimnAsInteger=9DimsAsInteger=3Ifargs.Length<1ThenConsole.WriteLine("Usage: sudoku_vb filename")ReturnEndIfTryDimenvAsNewGRBEnv()DimmodelAsNewGRBModel(env)' Create 3-D array of model variablesDimvarsAsGRBVar(,,)=NewGRBVar(n-1,n-1,n-1){}ForiAsInteger=0Ton-1ForjAsInteger=0Ton-1ForvAsInteger=0Ton-1DimstAsString="G_"&i&"_"&j&"_"&vvars(i,j,v)=model.AddVar(0.0,1.0,0.0,GRB.BINARY,st)NextNextNext' Add constraintsDimexprAsGRBLinExpr' Each cell must take one valueForiAsInteger=0Ton-1ForjAsInteger=0Ton-1expr=0ForvAsInteger=0Ton-1expr.AddTerm(1.0,vars(i,j,v))NextDimstAsString="V_"&i&"_"&jmodel.AddConstr(expr=1,st)NextNext' Each value appears once per rowForiAsInteger=0Ton-1ForvAsInteger=0Ton-1expr=0ForjAsInteger=0Ton-1expr.AddTerm(1.0,vars(i,j,v))NextDimstAsString="R_"&i&"_"&vmodel.AddConstr(expr=1,st)NextNext' Each value appears once per columnForjAsInteger=0Ton-1ForvAsInteger=0Ton-1expr=0ForiAsInteger=0Ton-1expr.AddTerm(1.0,vars(i,j,v))NextDimstAsString="C_"&j&"_"&vmodel.AddConstr(expr=1,st)NextNext' Each value appears once per sub-gridForvAsInteger=0Ton-1Fori0AsInteger=0Tos-1Forj0AsInteger=0Tos-1expr=0Fori1AsInteger=0Tos-1Forj1AsInteger=0Tos-1expr.AddTerm(1.0,vars(i0*s+i1,j0*s+j1,v))NextNextDimstAsString="Sub_"&v&"_"&i0&"_"&j0model.AddConstr(expr=1,st)NextNextNext' Fix variables associated with pre-specified cellsDimsrAsStreamReader=File.OpenText(args(0))ForiAsInteger=0Ton-1DiminputAsString=sr.ReadLine()ForjAsInteger=0Ton-1DimvalAsInteger=Microsoft.VisualBasic.Asc(input(j))-48-1' 0-basedIfval>=0Thenvars(i,j,val).LB=1.0EndIfNextNext' Optimize modelmodel.Optimize()' Write model to filemodel.Write("sudoku.lp")DimxAsDouble(,,)=model.Get(GRB.DoubleAttr.X,vars)Console.WriteLine()ForiAsInteger=0Ton-1ForjAsInteger=0Ton-1ForvAsInteger=0Ton-1Ifx(i,j,v)>0.5ThenConsole.Write(v+1)EndIfNextNextConsole.WriteLine()Next' Dispose of model and envmodel.Dispose()env.Dispose()CatcheAsGRBExceptionConsole.WriteLine("Error code: "&e.ErrorCode&". "&e.Message)EndTryEndSubEndClass
Help and Feedback