Результаты и обсуждение майского хабрасоревнования: делаем свой ГЛОНАСС

d1967ef164a3824ed8be8cb0da2e8387.jpgИтак, настало время подвести итоги прошедшего майского хабрасоревнования. Было прислано 49 решений, удовлетворяющих требованиям по оформлению и 8 решений вне конкурса (позже дедлайна, с ошибками оформления, на неверный адрес). Что-ж, посмотрим, кто что написал.Хотя в обсуждении топика с самой задачей люди переживали, что самое компактное и самое быстрое решение — это разные вещи, оказалось что решение победителя lany — является и самым компактным, удовлетворяющим всем требованиям. Решение Frommi было вдвое компактнее, 863 байта —, но не смогло пройти все тесты. Следующим шло решение ibessonov на 1613 байта —, но оно внезапно показало большую ошибку на первом тесте.

Если у меня читатели не найдут ошибки, тройка победителей будет выглядеть так:

lany — двойной победитель, 89.9448 баллов и самое компактное решение.Самый большой тест (2.4Гб) пройден за 0.61 секунду.Смотреть исходник //@lany public class G{java.io.InputStream I=System.in; int c, i=0, j, m, M; double[]S=new double[512], C=new double[512]; byte[]P=»111001100011101101».getBytes (), d=new byte[999999]; double N=4.0678884e13, L=29.9792458, X, Y, q, F, G, H=1e99; S[]z; S f, g; class S{double x, y, r, q, R, Q; S (){x=new Double (l ()); y=new Double (l ()); try{I.read (d); I.skip (9000001);}catch (Exception e){}q=L*o ()+L/2; Q=q*q; r=q-L; R=r*r; l ();}int o (){int o=0, p; for (;; o+=10){for (p=0; p<18;p++)if(P[p]!=d[o+p*10])break;if(p==18){while(d[--o]>48); return o+1;}}}}void u (double X, double Y){if (X*X+Y*Ys.Q)q+=X-s.Q; else if (X0? q: Q;}void b (double r, double R){if (r+R>q){double d=Math.abs (r*r-R*R+q*q)/2/q, h=Math.sqrt (r*r-d*d), x=f.x+X*d, y=f.y+Y*d; u (x-Y*h, y+X*h); u (x+Y*h, y-X*h);}}String l (){char[]o=new char[99]; int p=0, b; try{while ((b=I.read ())!=10)o[p++]=(char)b;}catch (Exception e){}return new String (o,0, p).trim ();}public static void main (String[]a){new G ();}G (){for (; i<512;i++){q=Math.PI*i/256;S[i]=Math.sin(q);C[i]=Math.cos(q);}c=new Short(l());z=new S[c];for(i=0;i0; i++)for (j=0; j0; j++){f=z[i]; g=z[j]; X=g.x-f.x; Y=g.y-f.y; q=Math.sqrt (X*X+Y*Y); X/=q; Y/=q; b (f.q, g.q); b (f.r, g.q); b (f.q, g.r); b (f.r, g.r);}double x=F, y=G, r=d (x, y), t=r<1e10?1e3:3e6,u,v,w,R;while(t>.1&&(i++<999||r>0)){R=r; X=x; Y=y; for (M=4; M<513&&R==r;M*=2){for(m=0;m0){j=m*512/M; u=x+S[j]*t; v=y+C[j]*t; if (u*u+v*v

import java.io.*; import java.util.ArrayList;

public class Main {

public static final boolean ADVANCED_MODE = true; public static final int MAX_POINTS = 50; public static final double PRECISION = 30; public static final int THRESHOLD = 300;

public static final String START_TOKEN = »111001100011101101»; public static final long DATA_LENGTH = 10000000; public static final long SPEED = 299792458; public static final long EARTH_R = 6378000; public static final long MIN_SAT_POS = 10000000; public static final long MAX_SAT_POS = 20000000; public static final int MIN_OFFSET = (int)((MIN_SAT_POS — EARTH_R) * DATA_LENGTH / SPEED); public static final int MAX_OFFSET = (int)((MAX_SAT_POS + EARTH_R) * DATA_LENGTH / SPEED);

public static void main (String args[ ]) { //long startTime = System.currentTimeMillis (); try { DataInputStream in = new DataInputStream (System.in); DataReader reader = new DataReader (in); Point result = null;

int q = reader.readInt (); ArrayList sats = new ArrayList(q); for (int i = 0; i < Math.min(q, MAX_POINTS); i++) { double x = reader.readDouble(); double y = reader.readDouble(); int offset = reader.readOffset();

double radius = ((double)SPEED / DATA_LENGTH * offset);

sats.add (new Circle (new Point (x, y), radius)); }

if (sats.size () == 2) { ArrayList points = sats.get (0).intersect (sats.get (1)); for (Point p: points) { result = p; break; } } else { if (ADVANCED_MODE) { result = advancedCalc (sats); } else { result = simpleCalc (sats); } }

System.out.println (result.x + » » + result.y); //long time = (System.currentTimeMillis () — startTime); //System.out.println («Time:» + time); } catch (Exception e) { System.out.println (e.getMessage ()); } }

public static Point findRefPoint (ArrayList sats){ ArrayList points = new ArrayList(); for (int i = 0; i < 2; i++) { for(int j = i + 1; j < 3; j++) { points.addAll(sats.get(i).intersect(sats.get(j))); } }

Point p0 = null, p1 = null, p2 = null; for (Point p: points) { for (Point t: points) { if (p1 == null && t!= p && p.distance (t) < THRESHOLD){ p1 = t; continue; } if(p1 != null && t != p && t != p1 && p.distance(t) < THRESHOLD){ p2 = t; break; } } if(p1 != null && p2 != null) { p0 = p; break; } else { p1 = null; p2 = null; } } return new Point((p0.x + p1.x + p2.x) / 3, (p0.y + p1.y + p2.y) / 3); }

public static Point advancedCalc (ArrayList sats){ ArrayList allPoints = new ArrayList(); for (int i = 0; i < sats.size() - 1; i++) { for(int j = i + 1; j < sats.size(); j++) { allPoints.addAll(sats.get(i).intersect(sats.get(j))); } }

int count = 0; double sumX = 0; double sumY = 0;

for (Point p: allPoints) { boolean containsInAll = true; for (Circle sat: sats){ if (! sat.hasPoint (p)) { containsInAll = false; break; } } if (containsInAll) { count++; sumX += p.x; sumY += p.y; } } return new Point (sumX / count, sumY / count); }

public static Point simpleCalc (ArrayList sats){ int count = 0; double sumX = 0; double sumY = 0;

Point refPoint = findRefPoint (sats); for (int i = 0; i < sats.size() - 1; i++) { for(int j = i + 1; j < sats.size(); j++) { for(Point p : sats.get(i).intersect(sats.get(j))) { if(refPoint.distance(p) < THRESHOLD) { count++; sumX += p.x; sumY += p.y; } } } } return new Point(sumX / count, sumY / count); }

public static class DataReader { private DataInputStream _in;

public DataReader (DataInputStream in){ _in = in; }

public int readOffset () throws Exception { byte firstByte = _in.readByte (); int offset = 1; while (_in.readByte () == firstByte) { offset++; } int needToSkip = ((MIN_OFFSET — offset) / 10) * 10; _in.skipBytes (needToSkip); offset += needToSkip;

byte[] buffer = new byte[MAX_OFFSET — offset]; _in.read (buffer); _in.skipBytes ((int) DATA_LENGTH — offset — buffer.length — 1 + 2);

StringBuilder sb = new StringBuilder (buffer.length / 10); for (int i = 0; i < buffer.length / 10; i++ ){ sb.append((char) buffer[i * 10]); }

int index = sb.indexOf (START_TOKEN)* 10;

return index + offset; }

public String readLine () throws Exception { StringBuilder sb = new StringBuilder (); char c; while ((c = (char)_in.readByte ()) != '\r') { sb.append©; } _in.readByte (); // read '\n' char return sb.toString (); }

public int readInt () throws Exception { String s = readLine (); return Integer.parseInt (s); }

public Double readDouble () throws Exception { String s = readLine (); return Double.parseDouble (s); } }

public static class Point { public double x; public double y;

public Point (double x, double y) { this.x = x; this.y = y; }

public double distance () { return Math.sqrt (x*x + y*y); }

public double distance (Point p) { return Math.sqrt (Math.pow (x — p.x, 2) + Math.pow (y — p.y, 2)); } }

public static class Circle { public Point center; public double radius; public Circle (Point p, double r) { center = p; radius = r; }

public ArrayList intersect (Circle c) { ArrayList result = new ArrayList();

double dx = c.center.x — center.x; double dy = c.center.y — center.y; double d = Math.sqrt ((dy * dy) + (dx * dx));

if (d < Math.abs(radius - c.radius)) { if(radius < c.radius) radius += Math.abs(radius - c.radius) - d + 0.1; else c.radius += Math.abs(radius - c.radius) - d + 0.1; } if (d > (radius + c.radius)) { double add = (d — (radius + c.radius))/ 2.0 + 0.1; radius += add; c.radius += add; }

if (d > (radius + c.radius) || d < Math.abs(radius - c.radius)) { //System.out.println("do not intersect"); return result; }

double a = ((radius * radius) — (c.radius * c.radius) + (d *d)) / (2.0 * d);

Point p2 = new Point (center.x + (dx * a/d), center.y + (dy * a/d));

double h = Math.sqrt ((radius * radius) — (a*a)); double rx = -dy * (h/d); double ry = dx * (h/d);

Point p = new Point (p2.x + rx, p2.y + ry); if (p.distance () <= EARTH_R) { result.add(p); } p = new Point(p2.x - rx, p2.y - ry); if(p.distance() <= EARTH_R) { result.add(p); }

return result; }

public boolean hasPoint (Point p) { double d = center.distance (p); return Math.abs (d — radius) <= PRECISION; } } } Staker — 83.3059 балла, еще чуть медленнее и чуть менее точно.Смотреть исходник //@Staker import java.io.BufferedInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.concurrent.LinkedBlockingQueue; import java.util.regex.Matcher; import java.util.regex.Pattern;

public class Staker { static final double M_PER_TICK = 29.9792458; static final double TICK_PER_M = 0.03335640951; static final double MAX_RANGE = 6378000; static final int MINIMUM_READ_SKIP = 120800; static final int MAX_READ_SIZE = 885000; static final int SEQ_SIZE = 10000001; static final int APPROXIMATION_MARGIN = 50; static final String START_COMPACT = »111001100011101101»; static final Pattern START_COMPACT_PATTERN = Pattern.compile (START_COMPACT, Pattern.LITERAL); static byte[] buf = new byte[SEQ_SIZE];

static long startTime = 0; static volatile boolean stop = false; static volatile boolean stopReader = false; static volatile Solution approximation = null;

static class Satelite { static final double HALFTICK_MARGIN = 0.001; static final double HALFTICK = 14.9896229; static final double HALFTICK_ALLOWED_RANGE = HALFTICK + 0.002;

int index; double x; double y; double range; double checkRange; Satelite (double x, double y, double range) { this (-1, x, y, range); } Satelite (int index, double x, double y, double range) { this.index = index; this.x = x; this.y = y; this.range = range; } double scoreSolution (Solution s) { double solutionRange = range (x, y, s.x, s.y); double score = 1; double delta = Math.abs (range — solutionRange); if (delta > HALFTICK_ALLOWED_RANGE) { double tempScore = 1/(1 + delta — HALFTICK_ALLOWED_RANGE); if (tempScore < score) { score = tempScore; } } return score; } Satelite halfTickCloser() {; return new Satelite(x, y, range - HALFTICK + HALFTICK_MARGIN); } Satelite halfTickFarther() { return new Satelite(x, y, range + HALFTICK - HALFTICK_MARGIN); } } static class Solution { double x; double y; double score; double spread; double count; Solution(double x, double y) { this.x = x; this.y = y; this.score = 0; this.spread = 0; this.count = 0; } Solution(Solution other) { this.x = other.x; this.y = other.y; this.score = other.score; this.spread = other.spread; this.count = other.count; } boolean check() { return range(0, 0, x, y) <= MAX_RANGE; } } static class Buffer extends Object implements CharSequence { final byte[] byteBuf; final int offset; final int len; Buffer(byte[] buf, int offset, int len) { this.offset = offset; this.len = len; this.byteBuf = buf; } @Override public char charAt(int arg0) { return (char)byteBuf[offset + arg0 * 10]; }

@Override public int length () { return len / 10; }

@Override public CharSequence subSequence (int arg0, int arg1) { return null; } } static double range (double x1, double y1, double x2, double y2) { return Math.sqrt ((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1)); } static ArrayList calc (Satelite s1, Satelite s2) { ArrayList result = new ArrayList(); double xT = s2.x — s1.x; double yT = s2.y — s1.y; double theta = Math.atan2(yT, xT); double d = Math.sqrt (xT*xT + yT*yT); double resX = (s1.range*s1.range — s2.range*s2.range + d*d)/(2*d); double resY = Math.sqrt (s1.range*s1.range — resX*resX); //System.out.format («Solution [%e %e]\n», resX, resY);

double resR = Math.sqrt (resX*resX + resY*resY); double resT1 = Math.atan2(resY, resX) + theta; double resT2 = Math.atan2(-resY, resX) + theta; double finalX1 = resR*Math.cos (resT1) + s1.x; double finalY1 = resR*Math.sin (resT1) + s1.y; double finalX2 = resR*Math.cos (resT2) + s1.x; double finalY2 = resR*Math.sin (resT2) + s1.y; Solution solution = new Solution (finalX1, finalY1); if (solution.check ()) { result.add (solution); } solution = new Solution (finalX2, finalY2); if (solution.check ()) { result.add (solution); } return result; } static int indexOf (byte[] buf, int len, byte[] pattern) { for (int i = 0; i < len - pattern.length; ++i) { int j; for (j = 0; j < pattern.length; ++j) { if (buf[i + j] != pattern[j]) { break; } } if (j == pattern.length) { return i; } }

return -1; } static Satelite readSatelite (BufferedInputStream in, Solution approximation, int index) throws IOException { double x = Double.parseDouble (readLine (in)); double y = Double.parseDouble (readLine (in)); in.read (buf, 0, SEQ_SIZE); // Handle \r\n EOL. if (buf[SEQ_SIZE — 1] == '\r') { in.mark (3); int c = in.read (); if (c!= '\n') { in.reset (); } } double satOriginDistance = range (0, 0, x, y); int headSkipSize = (int)((satOriginDistance — MAX_RANGE) * TICK_PER_M); headSkipSize -= (headSkipSize % 10); int readSize = MAX_READ_SIZE; boolean seen0 = false, seen1 = false; int shift = -1; while (! seen0 || ! seen1) { ++shift; if (buf[shift] == '0') seen0 = true; if (buf[shift] == '1') seen1 = true; } shift = shift % 10;

if (approximation!= null) { double approximateRange = range (x, y, approximation.x, approximation.y); headSkipSize = (int)(approximateRange * TICK_PER_M — APPROXIMATION_MARGIN); headSkipSize -= (headSkipSize % 10); readSize = 2 * APPROXIMATION_MARGIN + START_COMPACT.length () * 10; } headSkipSize += shift; Buffer seq = new Buffer (buf, headSkipSize, readSize); Matcher matcher = START_COMPACT_PATTERN.matcher (seq); if (! matcher.find ()) { headSkipSize = MINIMUM_READ_SKIP + shift; seq = new Buffer (buf, headSkipSize, MAX_READ_SIZE); matcher.reset (seq); matcher.find (); } int ind = matcher.start () * 10 + headSkipSize;

return new Satelite (index, x, y, ind * M_PER_TICK); }

static boolean checkSpread (ArrayList solutions) { if (solutions.size () < 2) return false; final double MAX_SPREAD = 400; double minX, minY, maxX, maxY; minX = minY = 10000000; maxX = maxY = -10000000; for (Solution sol : solutions) { if (sol.x > maxX) maxX = sol.x; if (sol.y > maxY) maxY = sol.y; if (sol.x < minX) minX = sol.x; if (sol.y < minY) minY = sol.y; } return range(minX, minY, maxX, maxY) < MAX_SPREAD; }

static ArrayList pickSingleCluster (ArrayList solutions) { final double MAX_SPREAD = 400; // Calculate mean only for solutions close to solution 1. Solution s1 = solutions.get (0); ArrayList result = new ArrayList(); result.add (s1); for (Solution sol: solutions) { if (sol == s1) continue; if (range (s1.x, s1.y, sol.x, sol.y) < MAX_SPREAD) { result.add(sol); } } return result; }

static Solution calculateApproximation (ArrayList solutions) { double totalX = 0, totalY = 0; double minX, minY, maxX, maxY; minX = minY = 10000000; maxX = maxY = -10000000; for (Solution sol: solutions) { totalX += sol.x; totalY += sol.y; if (sol.x > maxX) maxX = sol.x; if (sol.y > maxY) maxY = sol.y; if (sol.x < minX) minX = sol.x; if (sol.y < minY) minY = sol.y; } Solution sol = new Solution(totalX/solutions.size(), totalY/solutions.size()); sol.spread = range(minX, minY, maxX, maxY); sol.count = Math.pow(solutions.size(), 0.25); return sol; }

static ArrayList getSolutionGrid (double x, double y, double stepX, double stepY, ArrayList sateliteList) { ArrayList areaSolutions = new ArrayList(11×11); for (double xx = x — stepX*5; xx <= x + stepX*5.5; xx += stepX) { for (double yy = y - stepY*5; yy <= y + stepY*5.5; yy += stepY) { areaSolutions.add(new Solution(xx, yy)); } } return filterResults(areaSolutions, sateliteList); }

static Solution calculateSolution (Solution approximation, ArrayList sateliteList) { double step = 10; ArrayList areaSolutions; Solution current = approximation; do { areaSolutions = getSolutionGrid (current.x, current.y, step, step, sateliteList); if (areaSolutions.size () == 1 && current.score < 1 && areaSolutions.get(0).score > current.score) { // Found a better single approximation point. current = areaSolutions.get (0); } else { step /= 2; current = calculateApproximation (areaSolutions); } } while (areaSolutions.size () < 20 && step > 0.01); return current; } static void findSolutions ( int startIndex, ArrayList sateliteList, ArrayList result) { for (Satelite sat1: sateliteList) { for (Satelite sat2: sateliteList) { if (sat2.index <= sat1.index || sat2.index < startIndex) { continue; } result.addAll(calc(sat1, sat2)); result.addAll(calc(sat1.halfTickCloser(), sat2)); result.addAll(calc(sat1.halfTickFarther(), sat2)); result.addAll(calc(sat1, sat2.halfTickCloser())); result.addAll(calc(sat1.halfTickCloser(), sat2.halfTickCloser())); result.addAll(calc(sat1.halfTickFarther(), sat2.halfTickCloser())); result.addAll(calc(sat1, sat2.halfTickFarther())); result.addAll(calc(sat1.halfTickCloser(), sat2.halfTickFarther())); result.addAll(calc(sat1.halfTickFarther(), sat2.halfTickFarther())); } } } static ArrayList filterResults ( ArrayList solutions, ArrayList sateliteList) { ArrayList result = new ArrayList(); Solution approximation = null; double bestScore = 0; for (Solution sol: solutions) { double minScore = 1; for (Satelite satelite: sateliteList) { double score = satelite.scoreSolution (sol); if (score < minScore) { minScore = score; } } if (minScore > bestScore) { bestScore = minScore; approximation = sol; } if (minScore == 1) { result.add (sol); sol.score = 1; } } if (approximation!= null && result.size () == 0) { result.add (approximation); approximation.score = bestScore; } return result; } static String readLine (BufferedInputStream in) throws IOException { StringBuilder result = new StringBuilder (); int c = in.read (); while (c >=0 && c!= '\n' && c!= '\r') { result.append (Character.toChars©); c = in.read (); } if (c == '\r') { in.mark (3); int tmp = in.read (); if (tmp!= '\n') { in.reset (); } } return result.toString (); } static class SateliteReader extends Thread { LinkedBlockingQueue satelites; BufferedInputStream in; volatile int numSats = 0; SateliteReader (LinkedBlockingQueue satelites) { this.satelites = satelites; } @Override public void run () { try { BufferedInputStream in = new BufferedInputStream (System.in); numSats = Integer.parseInt (readLine (in)); for (int i = 0; i < numSats && !stopReader; ++i) { satelites.add(readSatelite(in, approximation, i)); } if (stopReader) { satelites.add(new Satelite(-1, 0, 0, 0)); } in.close(); } catch (NumberFormatException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }

static class Scorer extends Thread { volatile ArrayList sateliteList; ArrayList result; LinkedBlockingQueue scoreSatelites; Scorer (ArrayList sateliteList, ArrayList result, LinkedBlockingQueue scoreSatelites) { this.sateliteList = sateliteList; this.result = result; this.scoreSatelites = scoreSatelites; }

@Override public void run () { ArrayList localSateliteList = new ArrayList(); while (! stop) { scoreSatelites.drainTo (localSateliteList); Solution localApproximation = new Solution (approximation); localApproximation = calculateSolution (localApproximation, localSateliteList); approximation = localApproximation; Satelite sat = null; try { sat = scoreSatelites.take (); } catch (InterruptedException e) { return; } localSateliteList.add (sat); } stopReader = true; } }

static class ScoringDaemon extends Thread {

ScoringDaemon () { }

@Override public void run () { double pScore = 0; while (true) { try { Thread.sleep (0, 10000); } catch (InterruptedException e) { return; } long currTime = System.nanoTime (); long timePassed = currTime — startTime; double tScore = 10/(timePassed/1000000000.0 + 1.1); double curPScore; if (approximation == null) { curPScore = 0; } else if (approximation.spread > 1000) { curPScore = 0; } else if (approximation.spread > 0) { curPScore = 100/(approximation.spread/2.2 + 10); } else { curPScore = 100/(1/approximation.score+10.01); } if (curPScore > pScore) { pScore = curPScore; } if (pScore > 0 && timePassed > 4800000000L) { stop = true; } else if (pScore > tScore) { // Good time-precision balance. Stop on next iteration. stop = true; } if (stop) { return; } if (timePassed > 4900000000L) { // We have some score and 4.9 seconds passed. Stop now! complete (); } } } }

static void complete () { if (approximation!= null) { System.out.format (»%f %f», approximation.x, approximation.y); } else { System.out.print (»0 0»); } System.exit (0); }

public static void main (String[] args) throws IOException, InterruptedException{ startTime = System.nanoTime (); LinkedBlockingQueue readSatelites = new LinkedBlockingQueue(); LinkedBlockingQueue scoreSatelites = new LinkedBlockingQueue(); SateliteReader sateliteReader = new SateliteReader (readSatelites); sateliteReader.start (); //System.setIn (new FileInputStream (»/media/ramdisk/test3_hi_precision.in»)); //BufferedReader in = new BufferedReader (new InputStreamReader (System.in));

while (sateliteReader.numSats == 0) { Thread.sleep (0, 1000); } int numSats = sateliteReader.numSats; ArrayList sateliteList = new ArrayList(); ArrayList result = new ArrayList(); Scorer scorer = new Scorer (sateliteList, result, scoreSatelites); ScoringDaemon scoringDaemon = new ScoringDaemon (); int startIndex = 0; while (sateliteList.size () < numSats && !stopReader) { Satelite sat = readSatelites.take(); if (stopReader) break; sateliteList.add(sat); scoreSatelites.add(sat); if (sateliteList.size() > 1 && approximation == null) { findSolutions (startIndex, sateliteList, result); startIndex = sateliteList.size (); result = filterResults (result, sateliteList); if (checkSpread (result)) { approximation = calculateApproximation (result); if (numSats > 2) { scorer.start (); scoringDaemon.start (); } } } } scorer.interrupt (); if (approximation == null) { result = pickSingleCluster (result); approximation = calculateApproximation (result); } approximation = calculateSolution (approximation, sateliteList); complete (); } } С полной таблицей результатов можно ознакомится тут. Красным подсвечены результаты, выходящие за допустимые пределы (ошибка более 1000 метров, или время работы более 5 секунд.). Скачать все решения, их ответы и результаты компилации — можно тут. Некоторые догадались о возможности использовании внутреннего состояния MD5 — это позволяло ускорить расчет хэшей в 3 раза за счет того, что первые 128 байт хэшируемой строки не меняются и мы можем продолжать расчет только с 3-го 64-байтового блока. Но на практике это не понадобилось.Так как длина передаваемой кодовой последовательности — 1 млн бит, очевидно, что там будут уникальные последовательности не длиннее 19–20 бит (2^20>1 млн). Это позволяло заранее найти и хранить только эти уникальные маркеры (+ их сдвиг), и использовать их чтобы вычислять сдвиг кодовой последовательности вообще без вычисления MD5 в процессе выполнения программы.

Если использовать несколько таких маркеров — можно не читать все 10 мегабайт по каждому спутнику, а обходится лишь маленьким кусочком кодовой последовательности из входного потока и пропускать остальное для скорости. Конечно, это дало бы больший прирост производительности, если бы данные давались в файле (где можно сделать seek), а не потоке стандартного ввода.

Крайне важно использовать факт того, что принимаемая последовательность идет с бОльшей частотой. Это позволяет увеличить точность определния расстояния до спутника с 300 до 30 метров. Прочитав последовательность до первой единицы — мы однозначно определяем остаток деления на 10 от сдвига последовательности. Далее — искать маркеры можно уже по прореженной в 10 раз последовательности (для скорости).d8e35f4a1107d86bd5258990683cdcaa.png

Теперь мы знаем расстояние до спутника. Если бы оно не было округлено до ближайшего 100нс интервала (1 секунда/10МБит = 100нс = 29.9 метров) — то возможные точки расположения приемника лежали бы на окружности. С округлением — они лежат на кольце шириной в 29.9 метров. Если подходить к задаче геометрически — далее нам нужно найти пересечение N таких колец и окружности земли. Затем найти в полученной области возможных положений приемника точку, обеспечивающую минимальное математическое ожидание ошибки.

Реализация такого «точного» решения обещает быть громоздкой —, но оно может быть упрощено: например можно примерно находить множество допустимых точек на сетке с фиксированным шагом (например 0.1 метра) в районе приблизительного оптимума, можно использовать алгоритмы последовательного приближения, но тут нужна аккуратная целевая функция, чтобы не потерять точность когда у нас много «неудобных» спутников висят с одной стороны земли.

Надеюсь участники нам раскажут о своих других хитростях в решении.

Тесты 1–3 — простые тесты с количеством спутников 3–5.Тест 4 — 50 равномерно распределенных спутников.Тест 5 — максимальный тест, 255 равномерно распределенных спутников (2.4Гб данных).Тест 6 — 40 первых спутников неудобно висят с одной стороны земли и не позволяют построить точные координаты, затем идут 10 равномерно распределенных спутников.Скачать тесты можно тут (200Мб в архиве).

Очень много людей пострадало от невнимательности — несколько решений выводили отладочную информацию. Несколько решений выводило вещественные числа с запятой, а не точкой как было показано в примере. Несколько решений падало с различными ошибками. Там, где в таблице результатов вы видите очень большую ошибку — скорее всего в выходном файле не число.Тем не менее, некоторые вещи, о которых не было упомянуто в условии — я исправил. Одно решение пришло в кодировке cp1251 с русскими комментариями, и javac без дополнительных пинков не хотел проходить мимо русских комментариев. Решение было руками конвертировано в utf-8 и использован ключ компиляции -encoding UTF-8. В нескольких программах был указан package — его я закомментировал, для унификации процесса тестирования.

Решения вне конкурса также были протестированы, однако пока на таблицу результатов они не смогли повлиять — так что кусать локти пока не стоит. Причины «вне конкурса»: Redmoon — оформление (не по формату указан пользователь), strelok369 — оформление (не по формату указан пользователь), Agath — позже дедлайна, vadimmm — позже дедлайна, jwriter — позже дедлайна, xio4 — позже дедлайна, VadosM — позже дедлайна, oleksandr17 — неверный адрес отправки.

Скачать все решения вне конкурса, их ответы и результаты компиляции — можно тут

Будет интересно услышать дополнения и хитрости, используемые в решениях. Следующие 24 часа — для поиска участниками моих ошибок, не сомневаюсь, что это возможно. Если ничего изменяющего таблицу не будет найдено — результаты будут финализированы и приступим к вручению призов.

© Habrahabr.ru