잉크 블랏을 완화하기 위해 종이 접기 최적화


19

짙은 검정색 잉크가 프린터 용지의 흰색 시트 전체에 뿌려졌습니다! 확실한 해결책은 흑백 부분이 만나고 잉크가 퍼지면서 둘 다 회색이되도록 용지를 접는 것입니다. 그런 다음 용지가 모두 똑같이 회색이 될 때까지 펼치고 접습니다.

이러한 접힘을 만드는 가장 좋은 방법을 찾는 것이이 코딩 과제의 과제입니다. 이 Pastebin 에는 1과 0의 네 가지 크기의 격자가 포함되어 있습니다. 각 격자는 잉크를 산산조각 낸 용지를 나타내며 회색으로 변해야합니다. 0은 종이이고 1은 잉크입니다.

이 그리드에서는 선과 열 사이의 공간을 따라 가로 및 세로 접기 만 유효합니다. 접을 때 겹치는 값 쌍의 평균이 구해집니다. 접기는 한 번에 하나씩 수행되며 항상 펼쳐집니다. 접기는 용지 크기가 아니라 잉크 분배 만 변경합니다.

Rn은 n 번째 열 이후부터 그리드의 왼쪽 가장자리를 오른쪽으로 접는 것을 나타냅니다. Dn은 n 번째 행부터 시작하여 그리드의 위쪽 가장자리를 아래쪽으로 접는 것을 나타냅니다. (n은 1- 인덱싱 됨)

이 그리드가 주어지면

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

D1 접기는 "전체 열을 아래로 접은 다음 펼칩니다"를 의미합니다.

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

그러면 R2는

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

다른 R2는 아무것도 변경하지 않습니다.

목표는 매번 정확히 8 배를 사용하여 4 개의 그리드 각각에 대해 최상의 잉크 확산 폴딩 시퀀스를 찾는 알고리즘을 작성하는 것입니다. 접힘은 R 또는 D의 임의의 조합 일 수있다.

채점

제출 점수는 각 그리드에 대한 점수의 합계입니다. 모눈의 점수는 각 값과 평균 (절을 면적으로 나눈 값) 간의 절대 차이의 합입니다. 점수가 낮을수록 좋습니다. 0 점은 완벽하지만 8 배만으로는 불가능합니다.

답에 코드와 함께 네 개의 8 단계 폴딩 시퀀스를보고해야합니다. 알고리즘이 실제로 작동하는지 확인할 수 있습니다.

이 양식을 작성하십시오 :

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

다음 은 접기 시퀀스에 따라 점수를 계산하는 Python 스크립트입니다.

당연히 다른 사람의 시퀀스 제출을 복사해서는 안됩니다. 각 그리드의 시퀀스는 처음 생성 한 사람에만 속합니다.

설명

  • 이상적으로 알고리즘은 모든 그리드에서 잘 작동하지만 이러한 특정 알고리즘에 맞게 조정할 수 있습니다.

  • 시퀀스와 함께 코드를 제출해야합니다. 이기려면 아직 게시되지 않은 최소 단계의 8 단계 폴딩 시퀀스 세트와 공개 감시 기능을 갖춘 알고리즘이 필요합니다. 코드를 설명하고 난독 처리하지 마십시오.

  • 격자는 절대 음수를 포함해서는 안됩니다.

  • 표준 허점이 적용됩니다.


1
테스트 사례가 있고 참가자가 시퀀스를 제공하는 대신 시퀀스를 생성하는 코드를 제공하는 것이 더 좋다고 생각합니다.
justhalf

1
다른 옵션은 사람들에게 코드와 함께 얻은 시퀀스를 제공하도록 요청하지만 실제로 자신의 작업을 사용하여 코드를 생성한다는 증거로 코드의 해시 (SHA-256)를 제공하도록 요청하는 것입니다. 얼마 전에 그런 종류의 메커니즘을 본 것을 기억하지만 기억할 수 없습니다. 아무도 그 도전을 지적 할 수 있습니까?
justhalf

1
하드 코딩을 금지하는 또 다른 방법은 다른 테스트 사례에도 도전 과제를 공개하는 것입니다.
Howard

1
@ Calvin'sHobbies 일부 알고리즘은 다른 알고리즘보다 특정 그리드에서 더 잘 작동 할 수 있기 때문에 더 많은 테스트 사례를 선호합니다. 내가 할 수있는 일은 각 참가자가 벤치 마크 세트에 테스트 사례를 추가 할 수 있는 Vector Racing으로 수행 한 작업입니다 . 이 경우, 초기 참가자가 나중에 추가 된 테스트 케이스를 사용하여 코드를 다시 실행할 것으로 기대할 수 없으므로 모든 제출물을 테스트하고 채점해야합니다.
Martin Ender

1
@ Calvin'sHobbies Brute 힘은 (19 + 39) ^ 8 (일부 대칭에서 빼기)로 훨씬 더 실현 가능합니다.
Howard

답변:


8

파이썬

처음 몇 번의 접힘에 대해 여러 가지 접힘 조합을 철저히 시도한 다음 탐욕스러운 접근 방식을 사용하여 나머지 접힘을 수행합니다.

철저한 접근 방식은 중앙의 합리적 접힘 범위 내에서 제한되어 있기 때문에 가능한 최소한의 접힘을 무시하지 않으면서도 최소한의 주름을 무시하지 않아도됩니다.

내 맥북 에어에서 파이 를 사용하여 달렸다 .

답변:

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
좋아, 이제이 일을 그만 둘 수있어 이것은 정확히 내 알고리즘 일 것입니다.
Martin Ender

@bitpwner 여전히 그리드 평균으로 0.5를 사용하고 있지만 실제로 그리드에 따라 약간 다릅니다. ideone.com/5wbrOQ 에서 내 스크립트를 사용하면 총 103.31로 8.26, 17.71875, 44.61125 및 32.72를 얻습니다.
Calvin 's Hobbies

5

C, 16.344 (4 분 33 초)

지금까지 찾은 최고의 동작 : D6, D13, R19, D9, D11, R21, D10, R20

Monte Carlo와 언덕 등반을 혼합하여 사용합니다. 훨씬 더 빨리 달릴 수있었습니다.

#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;
}

바. 질문이 변경되었음을 알았습니다. 나중에이 문제를 해결해야합니다.
squeamish ossifrage

현재 40 * 20에 대해 16.34375의 괜찮은 점수를 얻고 있습니다.
Calvin 's Hobbies
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.