Я создаю программу в Scala, которая преобразует данные ДНК, хранящиеся в txt файлах, используя 8 бит на Char в файл, используя 2 бита на Char. Единственными символами, которые использует ДНК, являются T, C, A, G. Я хочу использовать 2 бита для каждого символа, где T = 00, C = 01, A = 10 и G = 11. Я хочу, чтобы это было настолько компактным, насколько возможно, без каких-либо потраченных впустую бит.
Прямо сейчас, я пишу 8 бит за Char вместо двух, которые я хочу использовать. Для метода outPut вы можете предложить любую манипуляцию с битами, которую я могу сделать, чтобы максимизировать пространство и использовать только два бита на символ?
Невозможно вывести меньше байта за раз. Вам нужно будет построить 8-битный символ из двухбитных писем dna, прежде чем писать символ.
Я не знаю Scala или Java, но в сочетании вашего кода и чего-то более похожего на C он будет выглядеть следующим образом:
def outPut(in: DataInputStream, out: DataOutputStream) {
var result = getChars(in)
int letter_count = 0 // whatever a plain old integer is called
char byte = 0
for(i <- 0 until result.length) {
if (result(i) == "A") {
byte = (byte << 2) | 2 // move byte over 2 bits and "or" in new bits
}else if(result(i) == "T") {
byte = (byte << 2) | 0 // the "| 0" part here actually does nothing
}else if(result(i) == "C") {
byte = (byte << 2) | 1
}else {
byte = (byte << 2) | 3
}
letter_count += 1
if (letter_count == 4) {
out.writeByte(byte)
letter_count = 0
byte = 0
}
}
}
Обратите внимание также на user3580294.
Чтобы перейти в другое направление (от 2-битного кодирования до кодирования символов):
def toLetter(x) {
if (x == 0)
return "T"
else if (x == 1)
return "C"
else if (x == 2)
return "A"
else if (x == 3)
return "G"
}
def outputLetters(in: DataInputStream, out: DataOutputStream) {
var twobit = getChars(in) // or however you read the file
for(i <- 0 until twobit.length) {
byte = twobit(i)
out.writeByte(toLetter((byte >> 6) & 3))
out.writeByte(toLetter((byte >> 4) & 3))
out.writeByte(toLetter((byte >> 2) & 3))
out.writeByte(toLetter( byte & 3))
}
}
Это предполагает, что число букв равномерно делится на 4. Чтобы преодолеть это ограничение, вам нужно будет хранить дополнительную информацию в файле. Это может быть либо количество букв в последнем байте (от 1 до 4), либо общее количество букв, представленных в файле (из которых можно вычислить количество букв в последнем байте).
Что-то еще немного scala-ish. Группируйте базы в четыре (возможно, меньше для последней) и сопоставляйте каждую последовательность из четырех символов с соответствующими значениями.
def makeBits(base:Char):Int = {
base match {
case 'T' => 0
case 'C' => 1
case 'A' => 2
case 'G' => 3
case _ => -1 // some error here
}
}
def packBits(bases:String):Int = {
var res:Int = 0
for (bits <- bases) { res = (res << 2) + makeBits(bits)}
res
}
val packed = "ATGCTTTADGCA".grouped(4).map(packBits)
Если вы можете записывать сразу несколько байтов одновременно, звучит как я, вам придется читать буквы в группах по 4 и использовать некоторое смещение бит, чтобы иметь возможность использовать все 8 бит на байт. Это может вызвать некоторые проблемы, хотя если количество букв не кратно 4, потому что у вас не будет способа отличить отступы от обычных букв...
(извините, я не знаю Scala, но этот алгоритм должен работать независимо от языка)
Так что-то вроде
// Store byte equivalents of ATCG in variables, or use an enum
// create new byte[4]
// Fill array with next 4 chars, ideally using something like Java InputStream.read(byte[] b) so you can read in groups
// create temp byte variable
for (int i = 3; i >= 0; i--) {
switch(b) {
case <byte equivalent of A>:
temp += <binary equivalent of A> << 3 - i;
// Repeat for other letters
// Write out temp