इंकबॉट्स को कम करने के लिए पेपर फोल्डिंग को ऑप्टिमाइज़ करें


19

गहरे काले रंग की स्याही ने आपके कागज के सफेद चादर पर छींटाकशी की है! स्पष्ट समाधान कागज को मोड़ना है ताकि काले और सफेद भाग मिलते हैं और दोनों स्याही के फैलते ही ग्रे हो जाते हैं। तब तक प्रकट न करें और जब तक आपका पेपर सभी समान रूप से ग्रे न हो जाए।

इन सिलवटों को बनाने का सबसे अच्छा तरीका ढूँढना इस कोडिंग चुनौती में आपका काम है। इस पास्टबिन में चार अलग-अलग आकार के ग्रिड हैं जिनमें शून्य और शून्य हैं। प्रत्येक ग्रिड स्याही छींटे कागज के एक टुकड़े का प्रतिनिधित्व करती है जिसे आपको ग्रे होना चाहिए। शून्य कागज हैं और लोग स्याही हैं।

इन ग्रिडों में, लाइनों और स्तंभों के बीच के रिक्त स्थानों के साथ केवल क्षैतिज और ऊर्ध्वाधर तह वैध हैं। जब एक फोल्ड बनाया जाता है तो ओवरलैपिंग वैल्यूज़ के जोड़े औसत हो जाते हैं। सिलवटों को एक बार में किया जाता है और हमेशा सामने लाया जाता है। सिलवटें केवल स्याही वितरण को बदलती हैं, न कि कागज के आकार को।

एनएन स्तंभ के बाद शुरू होकर आरएन ग्रिड के बाएं किनारे को दाईं ओर मोड़ने को दर्शाता है। डीएन, पंक्ति के बाद से शुरू होकर, नीचे की ओर ग्रिड के शीर्ष किनारे को मोड़ने को दर्शाता है। (एन 1-अनुक्रमित है)

उदाहरण

इस ग्रिड को देखते हुए

0 1 1 1
0 0 0 0
0 0 0 0

एक डी 1 फोल्ड का अर्थ है "संपूर्ण शीर्ष पंक्ति को नीचे की ओर मोड़ो"।

0 0.5 0.5 0.5
0 0.5 0.5 0.5
0   0   0   0

फिर एक आर 2 का उत्पादन करेगा

0.25 0.5 0.5 0.25
0.25 0.5 0.5 0.25
   0   0   0    0

और दूसरा R2 कुछ भी नहीं बदलेगा।

लक्ष्य

आपका लक्ष्य एक एल्गोरिथ्म लिखना है जो हर बार 8 गुना का उपयोग करके चार ग्रिड में से प्रत्येक के लिए सबसे अच्छा स्याही फैलाने वाला तह अनुक्रम पाता है । सिलवटों रुपये या डीएस के किसी भी संयोजन हो सकता है।

स्कोरिंग

आपके सबमिशन का स्कोर प्रत्येक ग्रिड के लिए आपके स्कोर का योग है। ग्रिड का स्कोर उसके प्रत्येक मान और उसके औसत (उसके क्षेत्र द्वारा विभाजित राशि) के बीच पूर्ण अंतर का योग है। कम अंक बेहतर हैं। 0 का स्कोर एकदम सही है, लेकिन केवल 8 तह में यह असंभव है।

आपको अपने उत्तर में अपने चार 8-स्टेप फोल्डिंग सीक्वेंस को अपने कोड के साथ रिपोर्ट करना होगा। ऐसा इसलिए है क्योंकि हम आपके एल्गोरिथ्म को वास्तव में काम करता है सत्यापित कर सकते हैं।

कृपया उन्हें इस रूप में रखें:

20*20R1D2R3D4R5D6R7D8
40*20R1D2R3D4R5D6R7D8
40*40R1D2R3D4R5D6R7D8
20*80R1D2R3D4R5D6R7D8

यहां एक पायथन स्क्रिप्ट है जो आपके तह अनुक्रमों को देखते हुए आपके अंकों की गणना करेगी।

स्वाभाविक रूप से आपको किसी और के अनुक्रम को प्रस्तुत नहीं करना चाहिए। प्रत्येक ग्रिड के अनुक्रम केवल उस व्यक्ति के होते हैं जिसने उन्हें पहली बार बनाया था।

स्पष्टीकरण

  • आदर्श रूप से आपका एल्गोरिदम किसी भी ग्रिड पर अच्छी तरह से काम करेगा, हालांकि आप इसे इन विशिष्ट लोगों के लिए दर्जी कर सकते हैं।

  • आपको अपने अनुक्रम के साथ अपना कोड जमा करना होगा। जीतने के लिए आपको 8-स्टेप फोल्डिंग सीक्वेंस के सबसे छोटे स्कोरिंग सेट की आवश्यकता होती है जो पहले से ही पोस्ट नहीं किया गया है, और एक एल्गोरिथ्म भी है जो सार्वजनिक जांच के लिए खड़ा है। अपना कोड स्पष्ट करें, इसे बाधित न करें।

  • ग्रिड में कभी भी ऋणात्मक संख्या नहीं होनी चाहिए।

  • मानक खामियां लागू होती हैं।


1
मुझे लगता है कि यह बेहतर है अगर आपके पास कुछ परीक्षण मामले हैं, और प्रतिभागियों को कोड देने की अपेक्षा की जाती है, जो केवल अनुक्रम देने के बजाय अनुक्रम का उत्पादन करता है।
11:10 बजे

1
एक अन्य विकल्प लोगों को यह बताने के लिए है कि उन्हें अपने कोड के साथ अनुक्रम प्राप्त करने के लिए कहा जाए, लेकिन उन्हें अपने कोड के हैश (जैसे SHA-256) को प्रमाण के रूप में प्रदान करने के लिए कहें कि वे वास्तव में अपने काम का उपयोग करके इसका उत्पादन करते हैं। मुझे कुछ समय पहले इस तरह का तंत्र देखकर याद आया, लेकिन मुझे याद नहीं आ रहा है। क्या कोई उस चुनौती की ओर इशारा कर सकता है?
बजे

1
हार्ड-कोडिंग को प्रतिबंधित करने का दूसरा तरीका यह है कि चुनौती को अन्य परीक्षण मामलों के लिए भी खुला रखा जाए।
हावर्ड

1
@ केल्विन हॉबीज मैं ईमानदारी से परीक्षण के मामलों का एक बड़ा सेट पसंद करूंगा, क्योंकि कुछ एल्गोरिदम दूसरों की तुलना में कुछ ग्रिड पर बेहतर किराया दे सकते हैं। आप क्या कर सकते हैं कि मैंने वेक्टर रेसिंग के साथ क्या किया है कि प्रत्येक भागीदार बेंचमार्क सेट में एक परीक्षण केस जोड़ सकता है। उस स्थिति में, आपको इसे सभी सबमिशन का परीक्षण करने और स्कोर करने के लिए अपने ऊपर ले जाना होगा, क्योंकि आप शुरुआती प्रतिभागियों से यह उम्मीद नहीं कर सकते हैं कि बाद में जोड़े गए परीक्षण मामलों के साथ उनके कोड को फिर से चलाएँ।
मार्टिन एंडर

1
@ केल्विन हॉबीस ब्रूट बल है (19 + 39) ^ 8 (शून्य से कुछ सममिति) जो अधिक संभव है।
हावर्ड

जवाबों:


8

अजगर

पहले कुछ सिलवटों के लिए सिलवटों के विभिन्न संयोजनों का प्रयास करता है, फिर बाकी सिलवटों को लालची दृष्टिकोण का उपयोग करता है।

संपूर्ण दृष्टिकोण केंद्र में सिलवटों के एक उचित सीमा के भीतर बंधा हुआ है, जैसे कि यह हमेशा के लिए नहीं लेगा, जबकि एक न्यूनतम न्यूनतम उपज के लिए कई संभावित सिलवटों को अनदेखा नहीं करेगा।

मेरी macbook हवा पर pypy का उपयोग कर भाग गया।

उत्तर:

20*20D9R15R6D11R10R9D10R11
40*20D6D13D9R19R21R20D11D10
40*40D21R21R11D19R23R20D23D15
20*80D33D47D40R10D39D41R9R11

आउटपुट:

Exhaustive folds levels: 3
Percentage pruned from sides from exhaustive folds: 0.2
Time taken: 4.016076s
Score: 7.91125
20*20D9R15R6D11R10R9D10R11

Exhaustive folds levels: 3
Percentage pruned from sides from exhaustive folds: 0.2
Time taken: 28.529278s
Score: 16.34375
40*20D6D13D9R19R21R20D11D10

Exhaustive folds levels: 3
Percentage pruned from sides from exhaustive folds: 0.25
Time taken: 98.430465s
Score: 42.13
40*40D21R21R11D19R23R20D23D15

Exhaustive folds levels: 3
Percentage pruned from sides from exhaustive folds: 0.25
Time taken: 234.873787s
Score: 32.30875
20*80D33D47D40R10D39D41R9R11

कुल स्कोर: 7.91125 + 16.34375 + 42.13 + 32.30875 = 98.69375

कोड:

import time, math
from collections import deque

numberOfFolds = 8 # Total number of folds

startTime = time.clock()

exec "grid = ("+"""
1 1 1 0 1 1 0 0 1 0 0 1 0 1 1 0 1 0 1 1
1 1 0 0 0 1 0 1 1 0 0 0 1 0 1 1 1 0 1 1
0 1 0 0 0 1 0 1 0 1 1 1 1 0 1 0 1 0 1 0
0 0 0 1 0 1 0 0 0 0 1 1 1 0 1 1 0 0 0 1
0 1 0 1 1 0 0 0 0 0 1 0 1 1 1 0 1 0 1 0
1 0 1 1 0 1 1 1 1 1 1 0 0 1 0 1 0 1 0 1
0 1 1 1 0 0 0 1 1 0 1 0 1 1 0 0 0 0 0 0
0 0 0 0 0 0 1 1 1 1 1 0 0 0 1 1 0 0 0 0
1 1 1 0 1 0 0 1 0 1 1 1 1 1 1 0 0 0 0 1
1 1 0 0 0 1 1 1 0 1 0 1 0 0 1 1 0 0 1 0
0 1 1 0 0 0 1 1 0 1 1 1 0 1 1 1 0 1 0 1
0 1 1 1 1 0 0 1 1 0 1 0 1 1 1 1 0 1 1 0
0 0 0 1 0 0 0 1 0 1 0 0 1 0 0 0 1 0 0 1
0 0 1 1 1 1 1 1 0 0 1 1 0 0 1 1 0 0 1 1
1 1 1 1 0 1 1 0 0 0 0 1 1 1 0 0 0 0 0 1
1 0 0 1 0 0 0 0 1 0 0 1 1 0 0 0 0 0 0 0
0 1 1 1 0 0 1 1 0 0 1 1 1 1 0 1 1 0 0 1
0 0 1 0 1 1 1 1 0 1 1 0 1 0 1 0 0 1 1 0
0 1 1 0 1 0 0 1 0 0 1 1 1 1 1 0 1 1 0 0
0 0 1 0 1 1 1 0 0 1 0 0 0 1 0 1 1 1 1 1 
""".replace(" ",",").replace("\n","],[")[2:-2]+")"

def getAverage(grid):
    count = total = 0
    for j in grid:
        for i in j:
            count += 1
            total += i
    return total/float(count)

def getScore(grid, average):
    score = 0
    for j in grid:
        for i in j:
            score += abs(average-i)
    return score

def downFoldedGrid(grid, row, width, height, copy=True):
    if copy: grid = [r[:] for r in grid]
    foldRange = min(row, height-row)
    for j in xrange(foldRange):
        rowRef1 = grid[row+j]
        rowRef2 = grid[row-1-j]
        for i in xrange(width):
            rowRef1[i] = rowRef2[i] = (rowRef1[i] + rowRef2[i]) * .5
    return grid

def downFoldedScore(grid, score, average, row, width, height):
    foldRange = min(row, height-row)
    average2  = 2*average
    for j in xrange(foldRange):
        rowRef1 = grid[row+j]
        rowRef2 = grid[row-1-j]
        a = b = c = 0
        for i in xrange(width):
            a = rowRef1[i] 
            b = rowRef2[i]
            c = a+b
            score += abs(average2-c) - abs(average-a) - abs(average-b)
    return score

def rightFoldedGrid(grid, column, width, height, copy=True):
    if copy: grid = [r[:] for r in grid]
    foldRange = min(column, width-column)
    for j in xrange(height):
        rowRef = grid[j]
        for i in xrange(foldRange):
            a = column+i
            b = column-1-i
            rowRef[a] = rowRef[b] = (rowRef[a] + rowRef[b]) * .5
    return grid

def rightFoldedScore(grid, score, average, column, width, height):
    foldRange = min(column, width-column)
    average2 = 2*average
    for j in xrange(height):
        rowRef = grid[j]
        a = b = c = 0
        for i in xrange(foldRange):
            a = rowRef[column+i]
            b = rowRef[column-1-i]
            c = a+b
            score += abs(average2-c) - abs(average-a) - abs(average-b)
    return score

def bestFoldsGreedy(grid, average, maxFolds, width, height):
    score  = getScore(grid, average)
    folds  = []
    append = folds.append
    for z in xrange(maxFolds):
        bestFold      = 0
        bestFoldScore = score
        bestFoldGrid  = grid
        for i in xrange(1, width): #Try all right folds
            foldScore = rightFoldedScore(grid, score, average, i, width, height)
            if foldScore < bestFoldScore:
                bestFold      = i
                bestFoldScore = foldScore
        for i in xrange(1, height): #Try all down folds
            foldScore = downFoldedScore(grid, score, average, i, width, height)
            if foldScore < bestFoldScore:
                bestFold      = -i
                bestFoldScore = foldScore
        if bestFold:
            append(bestFold)
            score = bestFoldScore
            if bestFold > 0: rightFoldedGrid(grid, bestFold, width, height, False)
            else:            downFoldedGrid(grid, -bestFold, width, height, False)
    return score, folds


# Get the height and width
height  = len(grid)
width   = len(grid[0])

# Transpose the grid if height > width for better locality of reference
transposed = False
if height > width:
    grid = [[grid[i][j] for i in range(height)] for j in range(width)]
    transposed = True
    height, width = width, height

# The exhaustive grids and folds attempted
exhaustiveGridsAndFolds = deque([(grid,[])])
popleft = exhaustiveGridsAndFolds.popleft
append  = exhaustiveGridsAndFolds.append

# Set the bounds to exhaustively test for
exhaustiveLevels   = 3
prunePadding       = [0.2, 0.25][width*height > 1000]
leftBound          = int(max(width*prunePadding, 1))
rightBound         = int(width*(1.0-prunePadding))
topBound           = int(max(height*prunePadding, 1))
bottomBound        = int(height*(1.0-prunePadding))

# Populate the exhaustive grids and folds
while 1:
    grid, folds = popleft()
    if len(folds) == exhaustiveLevels:
        append((grid, folds))
        break
    for i in xrange(leftBound, rightBound):
        if i not in folds:
            append((rightFoldedGrid(grid, i, width, height), folds+[i]))
    for i in xrange(topBound, bottomBound):
        if -i not in folds:
            append((downFoldedGrid(grid, i, width, height), folds+[-i]))

# Test all the exhaustive grids and folds greedily
average             = getAverage(grid)
bestFinalScore      = getScore(grid, average)
bestFinalFolds      = []
numberOfGreedyFolds = numberOfFolds-exhaustiveLevels
while exhaustiveGridsAndFolds:
    grid, exhaustiveFolds = popleft()
    finalScore, greedyFolds = bestFoldsGreedy(grid, average, numberOfGreedyFolds, width, height)
    if finalScore <= bestFinalScore:
        bestFinalScore = finalScore
        bestFinalFolds = exhaustiveFolds + greedyFolds


# Repeat the last fold till the total number of folds if needed
if len(bestFinalFolds) < numberOfFolds:
    bestFinalFolds += [bestFinalFolds[-1]]*(numberOfFolds-len(bestFinalFolds))

# Print the best result
foldsString = ""
down  = "D"
right = "R"
if transposed:
    down,  right  = right,  down
    width, height = height, width
for fold in bestFinalFolds:
    if   fold > 0: foldsString += right+str(fold)
    elif fold < 0: foldsString += down+str(-fold)
print "Exhaustive folds levels: " + str(exhaustiveLevels)
print "Percentage pruned from sides from exhaustive folds: " + str(prunePadding)
print "Time taken: " + str(time.clock()-startTime) + "s"
print "Score: " + str(bestFinalScore)
print str(width) + "*" + str(height) + foldsString

2
ठीक है, मैं अब इस पर काम करना बंद कर सकता हूं। यह बिल्कुल मेरा एल्गोरिदम होता।
मार्टिन एंडर

@bitpwner आप अभी भी ग्रिड औसत के रूप में 0.5 का उपयोग कर रहे हैं, लेकिन यह वास्तव में ग्रिड के आधार पर थोड़ा अलग है। Ideone.com/5wbrOQ पर मेरी स्क्रिप्ट के साथ आप कुल 103.31 के लिए 8.26, 17.71875, 44.61125, और 32.72 स्कोर कर रहे हैं।
कैल्विन के शौक

5

सी, 16.344 (4 मिनट 33 सेकंड)

अब तक मिली सबसे अच्छी चाल: D6, D13, R19, D9, D11, R21, D10, R20

मोंटे कार्लो और पहाड़ी चढ़ाई के मिश्रण का उपयोग करता है। बहुत तेजी से चलाने के लिए बनाया जा सकता है, मुझे यकीन है।

#include <stdio.h>
#include <stdlib.h>

/*

Best result so far: 16.344
D6,D13,R19,D9,D11,R21,D10,R20

real    4m33.027s
user    4m12.787s
sys 0m1.334s

*/

#define GRID_WIDTH   40
#define GRID_HEIGHT  20
#define GRID_SIZE    (GRID_WIDTH * GRID_HEIGHT)
#define NUM_FOLDS    8
#define MAX_VALUE    (1 << NUM_FOLDS)
#define TARGET_VALUE (MAX_VALUE / 2)

double score_grid(short *g) {
  int i, sum;
  for (i=sum=0; i<GRID_SIZE; i++) sum += abs(*g++ - TARGET_VALUE);
  return sum * 1.0 / MAX_VALUE;
}

void h_fold(short *g, int fold_row) {
  int x, y0, y1;
  if (fold_row<1 || fold_row>=GRID_HEIGHT) return;
  y1 = fold_row * GRID_WIDTH;
  y0 = y1 - GRID_WIDTH;
  while (y0>=0 && y1<GRID_SIZE) {
    for (x=0; x<GRID_WIDTH; x++) {
      g[y0+x] = g[y1+x] = (g[y0+x] + g[y1+x]) >> 1;
    }
    y0 -= GRID_WIDTH;
    y1 += GRID_WIDTH;
  }
}

void v_fold(short *g, int fold_col) {
  int y, x0, x1;
  if (fold_col<1 || fold_col>=GRID_WIDTH) return;
  x1 = fold_col;
  x0 = x1 - 1;
  while (x0>=0 && x1<GRID_WIDTH) {
    for (y=0; y<GRID_SIZE; y+=GRID_WIDTH) {
      g[y+x0] = g[y+x1] = (g[y+x0] + g[y+x1]) >> 1;
    }
    x0--;
    x1++;
  }
}

void print_grid(short *g) {
  int i=0, checksum=0;
  while (i<GRID_SIZE) {
    checksum += *g;
    printf("%3X",*g++);
    if ((++i) % GRID_WIDTH == 0) putchar('\n');
  }
  if (checksum != GRID_SIZE * TARGET_VALUE) printf("Bad total: %d\n",checksum);
}

void init_grid(short *g) {
  int i;
  static short *start_grid=0, *sg;
  if (!start_grid) {
    char *src = "11010110100011100000001000110001001101010111000100100100000101100000101111000010"
                "10110011111011111101101011111001000010101010110111000101000001011111101000011001"
                "10000111111001111011100101101001101100001110001101001011010011011110101000011100"
                "00110010100010100010110101001100110001100100111010000110100110001000110000111101"
                "01000001110000101000110101011011101010111110101010110000001011010010000011101000"
                "11111011111100100100100010111010111111000101011110000100111111111000110101101101"
                "00110100010111101111000011011010000110001001101010010101110010110111101001011111"
                "10110001101100001110010100110100010011011110100110000100100111101101000010011001"
                "00011100110100111101000000001000010100001101001011000101101001000100111100011010"
                "00010110001110011111100011101111011100111001110011111011010010000100101111101001";
    start_grid = malloc(GRID_SIZE * sizeof(short));
    for (i=0; i<GRID_SIZE; i++) start_grid[i] = (src[i]&1)<<NUM_FOLDS;
  }
  sg = start_grid;
  for (i=0; i<GRID_SIZE; i++) *g++ = *sg++;
}

double evaluate(int *moves) {
  short *grid;
  double score;
  int i, f;
  grid = malloc(GRID_SIZE * sizeof(short));
  init_grid(grid);
  for (i=0; i<NUM_FOLDS; i++) {
    f = moves[i];
    if (f>0) v_fold(grid,f);
    else h_fold(grid,-f);
  }
  score = score_grid(grid);
  free(grid);
  return score;
}


double optimize_folding(int *moves, double score) {
  int opt_cycle, i, which_fold, new_move, f1, f2, t;
  double s;

  for (opt_cycle=0; opt_cycle<1000; opt_cycle++) {
    for (i=0; i<NUM_FOLDS; i++) {
      which_fold = random() % NUM_FOLDS;
      do {
        if (random()&1) new_move = random() % (GRID_WIDTH-1) + 1;
        else new_move = -(random() % (GRID_HEIGHT-1) + 1);
      } while (moves[which_fold]==new_move);
      t = moves[which_fold];
      moves[which_fold] = new_move;
      s = evaluate(moves);
      if (s>score) moves[which_fold] = t;
      else score = s;
    }
    for (i=0; i<NUM_FOLDS; i++) {
      f1 = random() % NUM_FOLDS;
      do {
        f2 = random() % NUM_FOLDS;
      } while (f2==f1);
      t = moves[f1];
      moves[f1] = moves[f2];
      moves[f2] = t;
      s = evaluate(moves);
      if (s>score) {
        t = moves[f1];
        moves[f1] = moves[f2];
        moves[f2] = t;
      }
      else score = s;
    }
  }

  return score;
}

void show_moves(int *moves) {
  int i, m;
  for (i=0; i<NUM_FOLDS; i++) {
    m = moves[i];
    printf("%c%d%c",(m>0)?'R':'D',abs(m),((i+1)%NUM_FOLDS)?',':'\n');
  }
}

int main() {
  int i, j, moves[NUM_FOLDS], save_moves[NUM_FOLDS];
  double score, best_score = 1.0E+99;

  srandomdev();
  for (i=0; i<400; i++) {
    for (j=0; j<NUM_FOLDS; j++) {
            if (random()&1) moves[j] = random() % (GRID_WIDTH-1) + 1;
            else moves[j] = -(random() % (GRID_HEIGHT-1) + 1);
        }
        score = optimize_folding(moves, 1.0E+99);
        if (score<best_score) {
            best_score = score;
            for (j=0; j<NUM_FOLDS; j++) save_moves[j]=moves[j];
        }
    }
  printf("%.3lf\n",best_score);
  show_moves(save_moves);
  return 0;
}

बाह। बस ध्यान दिया कि प्रश्न बदल गया है। मुझे इसे बाद में ठीक करना होगा ...
स्क्वीश ossifrage

वर्तमान में मैं आपके 40 * 20 के लिए 16.34375 का एक सभ्य स्कोर प्राप्त कर रहा हूं।
केल्विन के शौक 23
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.