Nhờ giúp rút gọn 1 chuỗi số.

Liên hệ QC
À mà CRC16 sẽ cho ra kq có thể lớn nhất là 5 số đó bạn, max 32767 hay 65535.
Và kg thể tính ngược lại số ban đầu, tức chỉ mã hóa 1 chiều đó bạn.
Dùng được kg ?
Hàm này cùng 1 chuỗi thì cho ra cùng 1 kq phải k bạn?nếu cùng kq thì dùng được. Bạn hướng dẫn mình dùng hàm này với.
 
Uhm, chuỗi khác nhau cho ra Crc16 khác nhau.
Nhiều mênh mông, bạt ngàn source code cho hàm Crc16. Bạn google vba crc16, rồi chọn 1 cái.
Có rất nhiều biến thể của thuật toán Crc16,bạn kg cần hiểu sâu. Chỉ cần copy code về dùng
 
Chủ thớt copy đoạn code này về dùng thử:
Mã:
Option Explicit

Private Function CRC16(ByVal iCrc As Integer, ByVal uChar As Integer) As Integer
    Dim iCarry As Integer, I As Byte
    
    For I = 0 To 7
        iCarry = (iCrc And 1) Xor IIf(uChar And (2 ^ I), 1, 0)
        iCrc = (iCrc And &HFFFF&) \ 2
        If iCarry <> 0 Then iCrc = iCrc Xor &H8408
    Next I
    
    CRC16 = iCrc
End Function

Public Function CalcCRC16(ByVal strInput As String) As String
    Dim iCrc As Integer, I As Integer
    Dim strRet As String
  
    iCrc = &H8408
    For I = 1 To Len(strInput)
        iCrc = CRC16(iCrc, AscW(Mid$(strInput, I, 1)))
    Next
    
    strRet = Hex$(iCrc)
    For I = 1 To 4 - Len(strRet)
        strRet = "0" & strRet
    Next
    
    CalcCRC16 = strRet
End Function
Giả sử A1 là số đt, bạn gõ vào B1 (hay cell bất kỳ bạn muốn): =CalcCRC16(A1), xong muốn kéo muốn fill gì tùy ý ;)
 
Chơi cái này cũng vui!
Giải pháp: ngắt 10 chữ số thành 5 phần, mỗi phần 2 chữ số, tức chỉ cần mã hóa 100 con số từ số 00 đến số 99 là xong.
Cột A: chứa chữ số từ 00 đến 99
Cột B: chứa các ký tự thay thế.
Côt E: ghi số điện thoại hoc sinh muốn "mã hóa" :)
Dùng hàm tự tạo JoinText() của thầy @ndu96081631, cụ thể như sau:
Mã:
F2=JoinText("",VLOOKUP(T(IF({1},MID(E2,{1,3,5,7,9},2))),$A$2:$B$100,2,))
G2=IF(F2="","",TEXT(SUM(IFERROR(FIND(MID(F2,{1,2,3,4,5},1),$B$2:$B$100)*10^{8,6,4,2,0}*(ROW($1:$100)-1),)),REPT("0",10)))
Kết thúc bằng Ctrl+shift+Enter.

Thân
Cách làm này cũng hay nhưng mã hv có
Bạn thử 'ngâm cứu' cách này xem sao:

0913410111213141516171819
04608JKLMNOPQRS
0M73L
Chưa thể hiểu cái bản bạn đưa ra. Mong bạn giải thích thêm
 
Cách làm này cũng hay nhưng mã hv
"Có...." rồi gì nữa bạn? :)

Làm cho vui mà!
Thực ra, bảng mã này bạn có thể tự chế biến theo ý mình, ví dụ: nếu bạn không thích những ký tự nho nhỏ như: "'.,'"... thì bạn có thể thay bằng số 0->9 (như file kèm). Khi có bảng mã thì "Đổi ngược" hay "Đổi xuôi" tùy ý mình,

"Tự do tự tại không bị ràng buộc" hợp với bản tính của tôi.

Thân
 

File đính kèm

  • MaHoaSo.xlsb
    18 KB · Đọc: 11
Mình vừa tìm ra 1 cách khác, đơn giản hơn, dùng chính lớp Dictionary. Đọc source C/C++ của nó (scrrun.dll) phát hiện ra.
Dictionary có 1 hidden property tên là "HashVal", method trong source C/C++ là get_HashVal. Mục đích nó là sinh mã băm (hash value) cho Key property. Chúng ta có thể gọi nó qua hàm CallByName của VB/VBA.
Mã băm này không trùng nhau cho các key khác nhau, là duy nhất.
Bạn chủ thớt có thể dùng hàm nay tương tự như hàm CalcCRC16 ở trên.
Mã:
Option Explicit

Private m_dict As Scripting.Dictionary

Public Function GetHash(ByVal strKey As String) As String
    Dim I As Long
    Dim strHash As String

    If m_dict Is Nothing Then
        Set m_dict = New Scripting.Dictionary
    End If

    strHash = CallByName(m_dict, "HashVal", VbGet, strKey)
    strHash = Hex$(strHash)
    For I = 1 To 4 - Len(strHash)
        strHash = "0" & strHash
    Next

    GetHash = strHash
End Function

Source code C/C++ của HashVal property trong scrrun.dll
Mã:
  749 static DWORD get_str_hash(const WCHAR *str, CompareMethod method)
  750 {
  751     DWORD hash = 0;
  752
  753     if (str) {
  754         while (*str) {
  755             WCHAR ch;
  756
  757             ch = (method == TextCompare || method == DatabaseCompare) ? tolowerW(*str) : *str;
  758
  759             hash += (hash << 4) + ch;
  760             str++;
  761         }
  762     }
  763
  764     return hash % DICT_HASH_MOD;
  765 }
  766
  767 static DWORD get_num_hash(FLOAT num)
  768 {
  769     return (*((DWORD*)&num)) % DICT_HASH_MOD;
  770 }
  771
  772 static HRESULT get_flt_hash(FLOAT flt, LONG *hash)
  773 {
  774     if (isinf(flt)) {
  775         *hash = 0;
  776         return S_OK;
  777     }
  778     else if (!isnan(flt)) {
  779         *hash = get_num_hash(flt);
  780         return S_OK;
  781     }
  782
  783     /* NaN case */
  784     *hash = ~0u;
  785     return CTL_E_ILLEGALFUNCTIONCALL;
  786 }
  787
  788 static DWORD get_ptr_hash(void *ptr)
  789 {
  790     return PtrToUlong(ptr) % DICT_HASH_MOD;
  791 }
  792
  793 static HRESULT WINAPI dictionary_get_HashVal(IDictionary *iface, VARIANT *key, VARIANT *hash)
  794 {
  795     dictionary *This = impl_from_IDictionary(iface);
  796
  797     TRACE("(%p)->(%s %p)\n", This, debugstr_variant(key), hash);
  798
  799     V_VT(hash) = VT_I4;
  800     switch (V_VT(key))
  801     {
  802     case VT_BSTR|VT_BYREF:
  803     case VT_BSTR:
  804         V_I4(hash) = get_str_hash(get_key_strptr(key), This->method);
  805         break;
  806     case VT_UI1|VT_BYREF:
  807     case VT_UI1:
  808         V_I4(hash) = get_num_hash(V_VT(key) & VT_BYREF ? *V_UI1REF(key) : V_UI1(key));
  809         break;
  810     case VT_I2|VT_BYREF:
  811     case VT_I2:
  812         V_I4(hash) = get_num_hash(V_VT(key) & VT_BYREF ? *V_I2REF(key) : V_I2(key));
  813         break;
  814     case VT_I4|VT_BYREF:
  815     case VT_I4:
  816         V_I4(hash) = get_num_hash(V_VT(key) & VT_BYREF ? *V_I4REF(key) : V_I4(key));
  817         break;
  818     case VT_UNKNOWN|VT_BYREF:
  819     case VT_DISPATCH|VT_BYREF:
  820     case VT_UNKNOWN:
  821     case VT_DISPATCH:
  822     {
  823         IUnknown *src = (V_VT(key) & VT_BYREF) ? *V_UNKNOWNREF(key) : V_UNKNOWN(key);
  824         IUnknown *unk = NULL;
  825
  826         if (!src) {
  827             V_I4(hash) = 0;
  828             return S_OK;
  829         }
  830
  831         IUnknown_QueryInterface(src, &IID_IUnknown, (void**)&unk);
  832         if (!unk) {
  833             V_I4(hash) = ~0u;
  834             return CTL_E_ILLEGALFUNCTIONCALL;
  835         }
  836         V_I4(hash) = get_ptr_hash(unk);
  837         IUnknown_Release(unk);
  838         break;
  839     }
  840     case VT_DATE|VT_BYREF:
  841     case VT_DATE:
  842         return get_flt_hash(V_VT(key) & VT_BYREF ? *V_DATEREF(key) : V_DATE(key), &V_I4(hash));
  843     case VT_R4|VT_BYREF:
  844     case VT_R4:
  845         return get_flt_hash(V_VT(key) & VT_BYREF ? *V_R4REF(key) : V_R4(key), &V_I4(hash));
  846     case VT_R8|VT_BYREF:
  847     case VT_R8:
  848         return get_flt_hash(V_VT(key) & VT_BYREF ? *V_R8REF(key) : V_R8(key), &V_I4(hash));
  849     case VT_INT:
  850     case VT_UINT:
  851     case VT_I1:
  852     case VT_I8:
  853     case VT_UI2:
  854     case VT_UI4:
  855         V_I4(hash) = ~0u;
  856         return CTL_E_ILLEGALFUNCTIONCALL;
  857     default:
  858         FIXME("not implemented for type %d\n", V_VT(key));
  859         return E_NOTIMPL;
  860     }
  861
  862     return S_OK;
  863 }
 
Lần chỉnh sửa cuối:
"Có...." rồi gì nữa bạn? :)

Làm cho vui mà!
Thực ra, bảng mã này bạn có thể tự chế biến theo ý mình, ví dụ: nếu bạn không thích những ký tự nho nhỏ như: "'.,'"... thì bạn có thể thay bằng số 0->9 (như file kèm). Khi có bảng mã thì "Đổi ngược" hay "Đổi xuôi" tùy ý mình,

"Tự do tự tại không bị ràng buộc" hợp với bản tính của tôi.

Thân
Mình ghi
"Có...." rồi gì nữa bạn? :)

Làm cho vui mà!
Thực ra, bảng mã này bạn có thể tự chế biến theo ý mình, ví dụ: nếu bạn không thích những ký tự nho nhỏ như: "'.,'"... thì bạn có thể thay bằng số 0->9 (như file kèm). Khi có bảng mã thì "Đổi ngược" hay "Đổi xuôi" tùy ý mình,

"Tự do tự tại không bị ràng buộc" hợp với bản tính của tôi.

Thân
Ah d
"Có...." rồi gì nữa bạn? :)

Làm cho vui mà!
Thực ra, bảng mã này bạn có thể tự chế biến theo ý mình, ví dụ: nếu bạn không thích những ký tự nho nhỏ như: "'.,'"... thì bạn có thể thay bằng số 0->9 (như file kèm). Khi có bảng mã thì "Đổi ngược" hay "Đổi xuôi" tùy ý mình,

"Tự do tự tại không bị ràng buộc" hợp với bản tính của tôi.

Thân
Có các ký tự đặc biệt cũng hơn kỳ. Nhưng nghĩ lại thì cách này dùng vẫn được. Cảm ơn bạn rất nhiều.
Bài đã được tự động gộp:

Mình cảm ơn các ae rất nhiều đã nhiệt tình giúp đỡ mình. Mình sẽ áp dụng các gợi ý của các bạn , có gì bướm mắc mong các ae giúp đỡ thêm. Xin cảm ơn.
 
Lần chỉnh sửa cuối:
Đã đêm nhưng hy vọng là tư duy vẫn sáng suốt.

1. Theo nguyên lý Dirichlet thì có vấn đề.

Trong hàm CalcCRC16 thì iCrc là giá trị Integer, do CRC16 cũng chỉ trả về Integer, tức 2 bai. Từ
Mã:
strRet = Hex$(iCrc)
suy ra là strRet có nhiều nhất là 4 ký tự của hệ 16 (hex).
Từ
Mã:
    For I = 1 To 4 - Len(strRet)
        strRet = "0" & strRet
    Next
   
    CalcCRC16 = strRet

suy ra là CalcCRC16 luôn trả về chuỗi 4 ký tự trong hệ 16 (hex).

2 bai chỉ biểu diễn được nhiều nhất là 2^16 = 65536 SỐ - MÃ khác nhau từng đôi một.
Gọi các SỐ - MÃ đó là các ngăn kéo khác nhau.

Chỉ cần xét chuỗi bắt đầu từ "098", tức ít hơn rất nhiều, thì cũng có 9 999 999 số khác nhau từng đôi một (các số có 7 chữ số từ 0 000 001 tới 9 999 999). Bây giờ bỏ các số đó vào theo đúng ngăn kéo - SỐ - MÃ của mình thì theo nguyên lý Dirichlet có ít nhất 1 ngăn kéo có chứa ít nhất là 2 chuỗi khác nhau. Hay nói cách khác là có ít nhất 2 chuỗi khác nhau nhưng cùng MÃ.

Nếu ai còn chưa tin ông Dirichlet thì xem tập tin. Tôi mới chỉ xét khoảng 1 triệu số thay cho 9 999 999 số, có đầu là "098" thì đã thấy có ít nhất (vì liệt kê mỏi tay) 19 chuỗi khác nhau nhưng cùng MÃ do CalcCRC16 trả về.

Để phân biệt các chuỗi bắt đầu từ "098", tức ít hơn rất nhiều, thì phải dùng ít nhất 6 chữ số của hệ 16. Nếu 5 chữ số của hệ 16 thì chỉ có nhiều nhất là 2^20 = 1 048 576 MÃ. Đấy là chỉ xét các chuỗi có đầu là "098". Còn nếu xét các chuỗi có đầu là "09" thì số chuỗi sẽ là
99 999 999. Tức phải dùng tới 7 chữ số của hệ 16 (hex).

2. Nếu cho là get_HashVal trả về 2 MÃ khác nhau cho 2 số khác nhau thì chưa hiểu cách hoạt động của Dictionary.

Có rất nhiều số khác nhau mà get_HashVal trả về cùng một mã. Vậy thì Dictionary dùng cái mã ấy để làm gì khi nó lặp lại cho nhiều số? Dùng để tìm kiếm nhanh.

Cơ cấu như thế nào? Nếu ta thêm một giá trị SỐ A vào "đít to" thì trước tiên nó tính MÃ cho số A đó. Tiếp theo nó sẽ kiểm tra trong danh sách MÃ (danh sách 1) là đã có MÃ ấy chưa. Nếu chưa có thì nó thêm MÃ đã tính vào danh sách MÃ - danh sách 1 (ở thời điểm "chào buổi sáng" thì danh sách MÃ là trống), đồng thời nó tạo danh sách SỐ (danh sách 2) ứng với MÃ đó và thêm A vào danh sách 2 này, và thêm SỐ A vào Dictionary. Nếu MÃ đã có trong danh sách MÃ (danh sách 1) thì nó nhẩy tới danh sách SỐ ứng với MÃ đó (danh sách 2) và kiểm tra SỐ A có trong danh sách SỐ (danh sách 2) kia không. Nếu có rồi thì "đít to" trả về lỗi (Exists trả về TRUE). Nếu chưa có trong danh sách các SỐ ứng với MÃ (danh sách 2) thì "đít to" them SỐ A vào Dictionary, đồng thời thêm SỐ A vào danh sách SỐ ứng với MÃ được tính (danh sách 2).

Danh sách MÃ - danh sách 1 chỉ có 1 nhưng danh sách 2 sẽ có nhiều. Có bao nhiêu MÃ thì có bấy nhiêu danh sách 2, mỗi danh sách 2 đi kèm với 1 MÃ trong danh sách MÃ (danh sách 1), và trong mỗi danh sách 2 là các số khác nhau nhưng cùng Mã.

Tóm lại sẽ có nhiều số cùng chung MÃ, và những số đó nằm cùng danh sách 2 ứng với MÃ đó. Sau một hồi thêm số vào "đít to" thì trong nó sẽ có vd. 5 MÃ khác nhau - danh sách 1 có 5 MÃ khác nhau, và trong mỗi danh sách 2 từ 5 danh sách 2 sẽ có một số SỐ cùng MÃ. Để dễ phân tích ta giả sử là sau khi thêm 50 000 SỐ vào "đít to" thì ta có 50 MÃ khác nhau (danh sách 1 có 50 MÃ), và có 50 danh sách 2, mỗi danh sách ứng với 1 MÃ. Giả sử mỗi danh sách 2 có 1000 SỐ khác nhau (nhưng cùng MÃ). Khi ta thêm SỐ A mới thì "đít to" làm: tính MÃ cho Số A (1 lần), nhẩy vào và duyệt MÃ trong danh sách 1 - trường hợp xấu nhất phải kiểm tra 50 lần. Trường hợp xấu nhất là MÃ đã tồn tại thì lại phải nhẩy tới danh sách 2 ứng với MÃ đó và duyệt xem SỐ A đã có trong danh sách chưa. Trường hợp xấu nhất phải kiểm tra 1000 lần. Tổng cộng trong trường hợp xấu nhất phải tính và kiểm tra: 1 + 50 + 1000 = 1051 lần. Nếu không dùng MÃ thì phải kiểm tra 50 000 số - 50 000 lần.

Như vậy tuy có nhiều số cùng MÃ nhưng việc dùng MÃ sẽ giúp tìm kiếm nhanh. Còn nếu nói các số khác nhau sẽ có MÃ khác nhau thì có nghĩa là không hiểu được cơ cấu tìm kiếm của Dictionary.
---------------
Nếu ai muốn test hàm GetHash thì xem trong tập tin. Tôi đã chỉ ra 3 số khác nhau mà GetHash trả về cùng MÃ "01DA". Thực ra trong khoảng từ
"0 988 876 543" tới "0 989 925 118"
có tới 884 chuỗi mà GetHash trả về cùng MÃ "01DA". Nhưng liệt kê hết có lẽ đến sáng mai mất.
 

File đính kèm

  • GetHash.xlsm
    642.1 KB · Đọc: 8
Lần chỉnh sửa cuối:
1. Vì ban đầu chủ thớt đã yêu cầu 4 chữ số nên tôi mới dùng CRC16, và biết là có thể sẽ xảy ra trùng CRC. Tôi đã nói trước. Dùng CRC32 hay CRC64 sẽ không trùng nhưng lại không đúng yêu cầu của chủ thớt.
2. Nhanh nhẩu đoản, tưởng tìm ra bug của scrrun.dll, hì hì, nhưng không phải. HashVal chỉ là 1 phần trong bảng băm của dictionary. Source của lớp Dictionary và method Exists có thể xem ở đây:

Vậy thì không dùng cách HashVal được. Chủ thớt bỏ đi.
Tui dùng đt 10 số trước giờ nên không để ý, với đt 12 số, 13 số... sau này, bắt đầu từ mấy tới mấy vậy các bác, để tui test luôn, có thể chuyển qua CRC32
 
Lần chỉnh sửa cuối:
Đúng là ngớ ngẩn thiệt, cứ đi tìm giải pháp đao to búa lớn. Bản thân 1 số nó đã là duy nhất, thì chỉ cần đổi base cho nó nó thành biểu diễn ít ký tự hơn thì nó vẫn là duy nhất. Nên đổi base quách là xong
Có các base từ 2 (nhị phân) tới 10 (decimal) tới 16 (hex) tới 36, là phổ biến nhất. Tôi thử với nhiều base rồi, chọn base 36 vì nó cho ra nhiều nhất là 6 ký tự với dãy số đt 10 số hiện nay. Chủ thớt mà đòi 4 hay nhỏ hơn thì cu anh tui thua ;)
Trang web để các bạn thử đổi base:
Từ Excel 2013 trở lên đã có hàm BASE này, nếu chủ thớt dùng Excel2013 thì dùng Base(x, 36) để đổi qua Base 36 value, và Decimal để đổi ngược lại.
Nếu chủ thớt dùng Excel nhỏ hơn 2013 thì dùng hàm copy từ Gú gồ của tui. Đổi ngược lại thì chủ thớt tự viết nhé :)
Xin xem file đính kèm.
The end.
 

File đính kèm

  • GetHash.xlsm
    610.5 KB · Đọc: 11
Web KT
Back
Top Bottom