Как найти одинаковые byte [] -объекты в двух массивах одновременно? - PullRequest
6 голосов
/ 02 января 2012

Я пытаюсь осуществить коллизионную атаку на хэши (я посещаю курс «криптография»).Поэтому у меня есть два массива хэшей (= байтовые последовательности byte[]) и я хочу найти хэши, которые присутствуют в обоих массивах.После некоторых исследований и долгих размышлений я уверен, что лучшим решением на одноядерном компьютере будет HashSet (добавьте все элементы первого массива и проверьте с помощью contains, присутствуют ли уже элементы второго массива).

Однако я хочу реализовать параллельное решение, поскольку у меня есть доступ к машине с 8 ядрами и 12 ГБ оперативной памяти.Лучшее решение, которое я могу придумать, - это ConcurrentHashSet, который можно создать с помощью Collections.newSetFromMap(new ConcurrentHashMap<A,B>()).Используя эту структуру данных, я мог бы добавить все элементы первого массива параллельно и - после всех добавленных элементов - я могу одновременно проверять с помощью contains идентичные хеши.

Итак, мой вопрос: знаете ли выАлгоритм предназначен для этой конкретной задачи?Если нет, есть ли у вас опыт использования такого ConcurrentHashSet в отношении проблем и эффективной сложности времени выполнения?Или вы можете порекомендовать другую готовую структуру данных, которая может мне помочь?

PS: Если кого-то интересуют детали: я планирую использовать Skandium для распараллеливания моей программы.

Ответы [ 2 ]

5 голосов
/ 02 января 2012

Я думаю, что было бы полной тратой времени на использование любой формы HashMap. Я предполагаю, что вы вычисляете многобайтовые хэши различных данных, это уже hash es, больше нет необходимости выполнять их хеширование.

Хотя вы не заявляете это, я предполагаю, что ваши хеши byte последовательностей. Очевидно, что trie или dawg были бы идеальными для их хранения.

Я бы посоветовал вам реализовать trie/dawg и использовать его для хранения всех хэшей в первом массиве. Затем вы можете использовать все свои вычислительные мощности параллельно для поиска каждого элемента во втором массиве в этом trie. Блокировки не требуются.

Добавлена ​​

Вот простая реализация Dawg, которую я собрал вместе. Вроде работает.

public class Dawg {
  // All my children.
  Dawg[] children = new Dawg[256];
  // Am I a leaf.
  boolean isLeaf = false;

  // Add a new word.
  public void add ( byte[] word ) {
    // Finds its location, growing as necessary.
    Dawg loc = find ( word, 0, true );
    loc.isLeaf = true;
  }

  // String form.
  public void add ( String word ) {
    add(word.getBytes());
  }

  // Returns true if word is in the dawg.
  public boolean contains ( byte [] word ) {
    // Finds its location, no growing allowed.
    Dawg d = find ( word, 0, false );
    return d != null && d.isLeaf; 
  }

  // String form.
  public boolean contains ( String word ) {
    return contains(word.getBytes());
  }

  // Find the Dawg - growing the tree as necessary if requested.
  private Dawg find ( byte [] word, int i, boolean grow ) {
    Dawg child = children[word[i]];
    if ( child == null ) {
      // Not present!
      if ( grow ) {
        // Grow the tree.
        child = new Dawg();
        children[word[i]] = child;
      }
    }
    // Found it?
    if ( child != null ) {
      // More to find?
      if ( i < word.length - 1 ) {
        child = child.find(word, i+1, grow);
      }
    }
    return child;
  }

  public static void main ( String[] args ) {
    Dawg d = new Dawg();
    d.add("H");
    d.add("Hello");
    d.add("World");
    d.add("Hell");
    System.out.println("Hello is "+(d.contains("Hello")?"in":"out"));
    System.out.println("World is "+(d.contains("World")?"in":"out"));
    System.out.println("Hell is "+(d.contains("Hell")?"in":"out"));
    System.out.println("Hal is "+(d.contains("Hal")?"in":"out"));
    System.out.println("Hel is "+(d.contains("Hel")?"in":"out"));
    System.out.println("H is "+(d.contains("H")?"in":"out"));
  }
}

Добавлена ​​

Это может быть хорошим началом для параллельной версии без блокировки. Эти вещи, как известно, трудно проверить, поэтому я не могу гарантировать, что это сработает, но, на мой взгляд, это должно сработать.

import java.util.concurrent.atomic.AtomicReferenceArray;


public class LFDawg {
  // All my children.
  AtomicReferenceArray<LFDawg> children = new AtomicReferenceArray<LFDawg> ( 256 );
  // Am I a leaf.
  boolean isLeaf = false;

  // Add a new word.
  public void add ( byte[] word ) {
    // Finds its location, growing as necessary.
    LFDawg loc = find( word, 0, true );
    loc.isLeaf = true;
  }

  // String form.
  public void add ( String word ) {
    add( word.getBytes() );
  }

  // Returns true if word is in the dawg.
  public boolean contains ( byte[] word ) {
    // Finds its location, no growing allowed.
    LFDawg d = find( word, 0, false );
    return d != null && d.isLeaf;
  }

  // String form.
  public boolean contains ( String word ) {
    return contains( word.getBytes() );
  }

  // Find the Dawg - growing the tree as necessary if requested.
  private LFDawg find ( byte[] word, int i, boolean grow ) {
    LFDawg child = children.get( word[i] );
    if ( child == null ) {
      // Not present!
      if ( grow ) {
        // Grow the tree.
        child = new LFDawg();
        if ( !children.compareAndSet( word[i], null, child ) ) {
          // Someone else got there before me. Get the one they set.
          child = children.get( word[i] );
        }
      }
    }
    // Found it?
    if ( child != null ) {
      // More to find?
      if ( i < word.length - 1 ) {
        child = child.find( word, i + 1, grow );
      }
    }
    return child;
  }

  public static void main ( String[] args ) {
    LFDawg d = new LFDawg();
    d.add( "H" );
    d.add( "Hello" );
    d.add( "World" );
    d.add( "Hell" );
    System.out.println( "Hello is " + ( d.contains( "Hello" ) ? "in" : "out" ) );
    System.out.println( "World is " + ( d.contains( "World" ) ? "in" : "out" ) );
    System.out.println( "Hell is " + ( d.contains( "Hell" ) ? "in" : "out" ) );
    System.out.println( "Hal is " + ( d.contains( "Hal" ) ? "in" : "out" ) );
    System.out.println( "Hel is " + ( d.contains( "Hel" ) ? "in" : "out" ) );
    System.out.println( "H is " + ( d.contains( "H" ) ? "in" : "out" ) );
  }
}
0 голосов
/ 02 января 2012

Более простой подход состоит в том, чтобы просто разбить первый массив на N равных (или почти равных) частей (при 8 ядрах n = 8 кажется разумным). Затем решите программу «обычным» способом, посмотрев, присутствуют ли какие-либо хеши во 2-м массиве в N меньших под-первых массивах. Это можно сделать параллельно.

Тем не менее, я никогда не слышал о попытках / ладах раньше, и я нашел главное обсуждение увлекательным и информативным. (Я в основном работаю с числами, а не со словами)

Это предполагает, что байты [] хэшей имеют некоторую конечную, короткую длину, так что вы действительно можете разделить исходный файл для параллельной обработки. Это тот случай?

РЕДАКТИРОВАТЬ ДОБАВЛЕНО

Пример этой идеи см. В Графических камнях для графических процессоров , под редакцией Вэнь-Мей В. Ху, глава 11, в статье Лиговски, Рудницкого, Лю и Шмидта. Они распараллеливают массивный поиск в базе данных по последовательностям белков, разбивая огромную единую базу данных на множество меньших частей, а затем запускают нормальный алгоритм для каждого элемента. Мне нравится эта цитата. «Описанный алгоритм смущающе параллелен». В их случае они использовали CUDA и должны были оптимизировать память, но этот принцип все же должен применяться к многоядерным машинам.

СЛЕДУЮЩИЙ ПСЕВДОКОД. Я буду использовать списки для входящих байтов [], надеюсь, это нормально.

Оригинал, 1 основной метод

originalProcess(List<byte[]> list1, List<byte[]> list2) {
   HashSet<byte[]> bigHugeHashOfList1 = new HashSet<byte[]>();
   bigHugeHashOfList1.addAll(list1);
   for (byte[] hash : list2)
      if (bigHugeHashOfList1.contains(hash)
         // do something
}

Новый метод. Использует точно такой же метод процесса (позже). Здесь нет РАСЧЕТОВ или ПОПЫТОК ...

preprocess(List<byte[]> list1, List<byte[]> list2) {
   List<byte[]>[] splitLists = new ArrayList<byte[]>[8];
   for (int i=0; i<8; i++)
      splitLists[i] = new ArrayList<byte[]>();
   for (byte[] hash : list1) {
      int idx = hash[0]&7; // I'm taking the 3 low order bits, YMMV
      splitLists[idx].add(hash);
      // a minor speedup would be to create the HashSet here instead of in originalProcess()
   }

   // now, using your favorite parallel/concurrency technique,
   // do the equivalent of
   for (int i=0; i<8; i++)
      originalProcess(splitLists[i], list2);
}    
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...